Compare commits

..

No commits in common. "master" and "zig-0.13" have entirely different histories.

77 changed files with 1343 additions and 3770 deletions

View file

@ -15,28 +15,14 @@ https://github.com/neurocyte/flow/assets/1552770/97aae817-c209-4c08-bc65-0a0bf1f
# Download / Install # Download / Install
Binary release builds are found here: [neurocyte/flow/releases](https://github.com/neurocyte/flow/releases/latest)
Fetch and install the latest release to `/usr/local/bin` with the installation helper script:
```shell ```shell
curl -fsSL https://flow-control.dev/install | sh curl -fsSL https://flow-control.dev/install | sh
``` ```
Binary release builds are found here: [neurocyte/flow/releases](https://github.com/neurocyte/flow/releases/latest)
Nightly binary builds are found here: [neurocyte/flow-nightly/releases](https://github.com/neurocyte/flow-nightly/releases/latest) Nightly binary builds are found here: [neurocyte/flow-nightly/releases](https://github.com/neurocyte/flow-nightly/releases/latest)
Install latest nightly build and (optionally) specify the installation destination:
```
curl -fsSL https://flow-control.dev/install | sh -s -- --nightly --dest ~/.local/bin
```
See all avalable options for the installer script:
```
curl -fsSL https://flow-control.dev/install | sh -s -- --help
```
Or check your favorite local system package repository. Or check your favorite local system package repository.
[![Packaging status](https://repology.org/badge/vertical-allrepos/flow-control.svg)](https://repology.org/project/flow-control/versions) [![Packaging status](https://repology.org/badge/vertical-allrepos/flow-control.svg)](https://repology.org/project/flow-control/versions)
@ -45,7 +31,7 @@ Or check your favorite local system package repository.
Make sure your system meets the requirements listed above. Make sure your system meets the requirements listed above.
Flow builds with zig 0.14.0 at this time. Build with: Flow builds with zig 0.13 at this time. Build with:
```shell ```shell
zig build -Doptimize=ReleaseSafe zig build -Doptimize=ReleaseSafe

View file

@ -17,13 +17,6 @@ pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Run unit tests"); const test_step = b.step("test", "Run unit tests");
const lint_step = b.step("lint", "Run lints"); const lint_step = b.step("lint", "Run lints");
var version = std.ArrayList(u8).init(b.allocator);
defer version.deinit();
gen_version(b, version.writer()) catch {
version.clearAndFree();
version.appendSlice("unknown") catch {};
};
return (if (release) &build_release else &build_development)( return (if (release) &build_release else &build_development)(
b, b,
run_step, run_step,
@ -36,7 +29,6 @@ pub fn build(b: *std.Build) void {
use_llvm, use_llvm,
pie, pie,
gui, gui,
version.items,
); );
} }
@ -52,7 +44,6 @@ fn build_development(
use_llvm: ?bool, use_llvm: ?bool,
pie: ?bool, pie: ?bool,
gui: bool, gui: bool,
version: []const u8,
) void { ) void {
const target = b.standardTargetOptions(.{ .default_target = .{ .abi = if (builtin.os.tag == .linux and !tracy_enabled) .musl else null } }); const target = b.standardTargetOptions(.{ .default_target = .{ .abi = if (builtin.os.tag == .linux and !tracy_enabled) .musl else null } });
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
@ -72,7 +63,6 @@ fn build_development(
use_llvm, use_llvm,
pie, pie,
gui, gui,
version,
); );
} }
@ -88,7 +78,6 @@ fn build_release(
use_llvm: ?bool, use_llvm: ?bool,
pie: ?bool, pie: ?bool,
_: bool, //gui _: bool, //gui
version: []const u8,
) void { ) void {
const targets: []const std.Target.Query = &.{ const targets: []const std.Target.Query = &.{
.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl }, .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl },
@ -102,8 +91,11 @@ fn build_release(
}; };
const optimize = .ReleaseFast; const optimize = .ReleaseFast;
var version = std.ArrayList(u8).init(b.allocator);
defer version.deinit();
gen_version(b, version.writer()) catch unreachable;
const write_file_step = b.addWriteFiles(); const write_file_step = b.addWriteFiles();
const version_file = write_file_step.add("version", version); const version_file = write_file_step.add("version", version.items);
b.getInstallStep().dependOn(&b.addInstallFile(version_file, "version").step); b.getInstallStep().dependOn(&b.addInstallFile(version_file, "version").step);
for (targets) |t| { for (targets) |t| {
@ -128,7 +120,6 @@ fn build_release(
use_llvm, use_llvm,
pie, pie,
false, //gui false, //gui
version,
); );
if (t.os_tag == .windows) if (t.os_tag == .windows)
@ -147,7 +138,6 @@ fn build_release(
use_llvm, use_llvm,
pie, pie,
true, //gui true, //gui
version,
); );
} }
} }
@ -167,7 +157,6 @@ pub fn build_exe(
use_llvm: ?bool, use_llvm: ?bool,
pie: ?bool, pie: ?bool,
gui: bool, gui: bool,
version: []const u8,
) void { ) void {
const options = b.addOptions(); const options = b.addOptions();
options.addOption(bool, "enable_tracy", tracy_enabled); options.addOption(bool, "enable_tracy", tracy_enabled);
@ -194,8 +183,7 @@ pub fn build_exe(
}; };
const wf = b.addWriteFiles(); const wf = b.addWriteFiles();
const version_file = wf.add("version", version); const version_info_file = wf.add("version", version_info.items);
const version_info_file = wf.add("version_info", version_info.items);
const vaxis_dep = b.dependency("vaxis", .{ const vaxis_dep = b.dependency("vaxis", .{
.target = target, .target = target,
@ -225,12 +213,7 @@ pub fn build_exe(
}); });
const thespian_mod = thespian_dep.module("thespian"); const thespian_mod = thespian_dep.module("thespian");
const cbor_mod = thespian_dep.module("cbor");
const cbor_dep = thespian_dep.builder.dependency("cbor", .{
.target = target,
.optimize = optimize_deps,
});
const cbor_mod = cbor_dep.module("cbor");
const tracy_dep = if (tracy_enabled) thespian_dep.builder.dependency("tracy", .{ const tracy_dep = if (tracy_enabled) thespian_dep.builder.dependency("tracy", .{
.target = target, .target = target,
@ -305,10 +288,6 @@ pub fn build_exe(
.root_source_file = b.path("src/color.zig"), .root_source_file = b.path("src/color.zig"),
}); });
const bin_path_mod = b.createModule(.{
.root_source_file = b.path("src/bin_path.zig"),
});
const Buffer_mod = b.createModule(.{ const Buffer_mod = b.createModule(.{
.root_source_file = b.path("src/buffer/Buffer.zig"), .root_source_file = b.path("src/buffer/Buffer.zig"),
.imports = &.{ .imports = &.{
@ -424,16 +403,6 @@ pub fn build_exe(
}, },
}); });
const git_mod = b.createModule(.{
.root_source_file = b.path("src/git.zig"),
.imports = &.{
.{ .name = "thespian", .module = thespian_mod },
.{ .name = "cbor", .module = cbor_mod },
.{ .name = "shell", .module = shell_mod },
.{ .name = "bin_path", .module = bin_path_mod },
},
});
const ripgrep_mod = b.createModule(.{ const ripgrep_mod = b.createModule(.{
.root_source_file = b.path("src/ripgrep.zig"), .root_source_file = b.path("src/ripgrep.zig"),
.imports = &.{ .imports = &.{
@ -461,7 +430,6 @@ pub fn build_exe(
.{ .name = "syntax", .module = syntax_mod }, .{ .name = "syntax", .module = syntax_mod },
.{ .name = "dizzy", .module = dizzy_dep.module("dizzy") }, .{ .name = "dizzy", .module = dizzy_dep.module("dizzy") },
.{ .name = "fuzzig", .module = fuzzig_dep.module("fuzzig") }, .{ .name = "fuzzig", .module = fuzzig_dep.module("fuzzig") },
.{ .name = "git", .module = git_mod },
}, },
}); });
@ -501,7 +469,6 @@ pub fn build_exe(
.{ .name = "Buffer", .module = Buffer_mod }, .{ .name = "Buffer", .module = Buffer_mod },
.{ .name = "keybind", .module = keybind_mod }, .{ .name = "keybind", .module = keybind_mod },
.{ .name = "shell", .module = shell_mod }, .{ .name = "shell", .module = shell_mod },
.{ .name = "git", .module = git_mod },
.{ .name = "ripgrep", .module = ripgrep_mod }, .{ .name = "ripgrep", .module = ripgrep_mod },
.{ .name = "theme", .module = themes_dep.module("theme") }, .{ .name = "theme", .module = themes_dep.module("theme") },
.{ .name = "themes", .module = themes_dep.module("themes") }, .{ .name = "themes", .module = themes_dep.module("themes") },
@ -544,8 +511,6 @@ pub fn build_exe(
exe.root_module.addImport("input", input_mod); exe.root_module.addImport("input", input_mod);
exe.root_module.addImport("syntax", syntax_mod); exe.root_module.addImport("syntax", syntax_mod);
exe.root_module.addImport("color", color_mod); exe.root_module.addImport("color", color_mod);
exe.root_module.addImport("bin_path", bin_path_mod);
exe.root_module.addImport("version", b.createModule(.{ .root_source_file = version_file }));
exe.root_module.addImport("version_info", b.createModule(.{ .root_source_file = version_info_file })); exe.root_module.addImport("version_info", b.createModule(.{ .root_source_file = version_info_file }));
if (target.result.os.tag == .windows) { if (target.result.os.tag == .windows) {
@ -588,8 +553,6 @@ pub fn build_exe(
check_exe.root_module.addImport("input", input_mod); check_exe.root_module.addImport("input", input_mod);
check_exe.root_module.addImport("syntax", syntax_mod); check_exe.root_module.addImport("syntax", syntax_mod);
check_exe.root_module.addImport("color", color_mod); check_exe.root_module.addImport("color", color_mod);
check_exe.root_module.addImport("bin_path", bin_path_mod);
check_exe.root_module.addImport("version", b.createModule(.{ .root_source_file = version_file }));
check_exe.root_module.addImport("version_info", b.createModule(.{ .root_source_file = version_info_file })); check_exe.root_module.addImport("version_info", b.createModule(.{ .root_source_file = version_info_file }));
check_step.dependOn(&check_exe.step); check_step.dependOn(&check_exe.step);
@ -620,7 +583,7 @@ pub fn build_exe(
}); });
lint_step.dependOn(&lints.step); lint_step.dependOn(&lints.step);
b.default_step.dependOn(lint_step); // b.default_step.dependOn(lint_step);
} }
fn gen_version_info( fn gen_version_info(

View file

@ -1 +1 @@
0.14.0 0.13.0

View file

@ -1,42 +1,41 @@
.{ .{
.name = .flow, .name = "flow",
.version = "0.2.0", .version = "0.2.0",
.minimum_zig_version = "0.14.0", .minimum_zig_version = "0.13.0",
.fingerprint = 0x52c0d670590aa80f,
.dependencies = .{ .dependencies = .{
.syntax = .{ .path = "src/syntax" }, .syntax = .{ .path = "src/syntax" },
.flags = .{ .flags = .{
.url = "https://github.com/n0s4/flags/archive/372501d1576b5723829bcba98e41361132c7b618.tar.gz", .url = "https://github.com/n0s4/flags/archive/b3905aa990719ff567f1c5a2f89e6dd3292d8533.tar.gz",
.hash = "flags-0.8.0-AAAAAJV0AACuGBBnpUnHqZzAhoGTp4ibFROBQQQZGRqx", .hash = "1220930a42f8da3fb7f723e3ad3f6dcc6db76327dd8d26274566423192d53e91b2bb",
}, },
.dizzy = .{ .dizzy = .{
.url = "https://github.com/neurocyte/dizzy/archive/455d18369cbb2a0458ba70be919cd378338d695e.tar.gz", .url = "https://github.com/neurocyte/dizzy/archive/455d18369cbb2a0458ba70be919cd378338d695e.tar.gz",
.hash = "dizzy-1.0.0-AAAAAM1wAAAiDbx_6RwcVEOBk8p2XOu8t9WPNc3K7kBK", .hash = "1220220dbc7fe91c1c54438193ca765cebbcb7d58f35cdcaee404a9d2245a42a4362",
}, },
.thespian = .{ .thespian = .{
.url = "https://github.com/neurocyte/thespian/archive/42a98a44e7590fd6e952b75523e5c0f4fa0e6c97.tar.gz", .url = "https://github.com/neurocyte/thespian/archive/9ca04ddfc715e0f7d29d3f6b39269ad9bf174230.tar.gz",
.hash = "thespian-0.0.1-owFOjnUTBgBUlBtQ-SbN_6S7bXE6e2mKmCgk4A-RGBMA", .hash = "1220b05b5949454bf155a802d5034c060431b8bf59f9d4d2d5241397e9fd201d78d9",
}, },
.themes = .{ .themes = .{
.url = "https://github.com/neurocyte/flow-themes/releases/download/master-ac2e3fe2df3419b71276f86fa9c45fd39d668f23/flow-themes.tar.gz", .url = "https://github.com/neurocyte/flow-themes/releases/download/master-59bf204551bcb238faddd06779063570e7e6d431/flow-themes.tar.gz",
.hash = "N-V-__8AAEtaFwAjAHCmWHRCrBxL7uSG4hQiIsSgS32Y67K6", .hash = "12209a213a392ea80ea5c7dde125e7161bb0278ac4b99db9df2b7af783710bcb09f7",
}, },
.fuzzig = .{ .fuzzig = .{
.url = "https://github.com/fjebaker/fuzzig/archive/44c04733c7c0fee3db83672aaaaf4ed03e943156.tar.gz", .url = "https://github.com/fjebaker/fuzzig/archive/0fd156d5097365151e85a85eef9d8cf0eebe7b00.tar.gz",
.hash = "fuzzig-0.1.1-AAAAALNIAQBmbHr-MPalGuR393Vem2pTQXI7_LXeNJgX", .hash = "122019f077d09686b1ec47928ca2b4bf264422f3a27afc5b49dafb0129a4ceca0d01",
}, },
.vaxis = .{ .vaxis = .{
.url = "https://github.com/neurocyte/libvaxis/archive/386e554f275c82ebfde7f15765687aaa6e89f6f6.tar.gz", .url = "https://github.com/neurocyte/libvaxis/archive/e518e139417a9773f59624961b02e05b8fffff35.tar.gz",
.hash = "vaxis-0.1.0-BWNV_FneCADwINyXfMcWHjv_vpQR-VOzrGSmH6BEP40y", .hash = "122045fec2cedf3f68c20f961c42f3ca1e901238aec5a0c7b3b632cd21ce3b621f76",
}, },
.zeit = .{ .zeit = .{
.url = "https://github.com/rockorager/zeit/archive/8fd203f85f597f16e0a525c1f1ca1e0bffded809.tar.gz", .url = "https://github.com/rockorager/zeit/archive/8fd203f85f597f16e0a525c1f1ca1e0bffded809.tar.gz",
.hash = "zeit-0.0.0-AAAAACVbAgAiIzg1rccZU1qOfO_dKQKme7-37xmEQcqc", .hash = "122022233835adc719535a8e7cefdd2902a67bbfb7ef198441ca9ce89c0593f488c2",
}, },
.win32 = .{ .win32 = .{
.url = "https://github.com/marlersoft/zigwin32/archive/e8739b32a33ce48a3286aba31918b26a9dfc6ef0.tar.gz", .url = "https://github.com/marlersoft/zigwin32/archive/259b6f353a48968d7e3171573db4fd898b046188.tar.gz",
.hash = "zigwin32-25.0.28-preview-AAAAAEEl_AMhnKSs-lgEyjmUX5JVTpNQewd8A2Bbekwc", .hash = "1220925614447b54ccc9894bbba8b202c6a8b750267890edab7732064867e46f3217",
.lazy = true, .lazy = true,
}, },
}, },

View file

@ -16,7 +16,7 @@ pub const VTable = struct {
pub fn to_owned(pimpl: anytype) Self { pub fn to_owned(pimpl: anytype) Self {
const impl = @typeInfo(@TypeOf(pimpl)); const impl = @typeInfo(@TypeOf(pimpl));
const child: type = impl.pointer.child; const child: type = impl.Pointer.child;
return .{ return .{
.ptr = pimpl, .ptr = pimpl,
.vtable = comptime &.{ .vtable = comptime &.{
@ -56,7 +56,7 @@ var none = {};
pub fn to_unowned(pimpl: anytype) Self { pub fn to_unowned(pimpl: anytype) Self {
const impl = @typeInfo(@TypeOf(pimpl)); const impl = @typeInfo(@TypeOf(pimpl));
const child: type = impl.pointer.child; const child: type = impl.Pointer.child;
return .{ return .{
.ptr = pimpl, .ptr = pimpl,
.vtable = comptime &.{ .vtable = comptime &.{
@ -79,7 +79,7 @@ pub fn to_unowned(pimpl: anytype) Self {
pub fn bind(pimpl: anytype, comptime f: *const fn (ctx: @TypeOf(pimpl), from: tp.pid_ref, m: tp.message) tp.result) Self { pub fn bind(pimpl: anytype, comptime f: *const fn (ctx: @TypeOf(pimpl), from: tp.pid_ref, m: tp.message) tp.result) Self {
const impl = @typeInfo(@TypeOf(pimpl)); const impl = @typeInfo(@TypeOf(pimpl));
const child: type = impl.pointer.child; const child: type = impl.Pointer.child;
return .{ return .{
.ptr = pimpl, .ptr = pimpl,
.vtable = comptime &.{ .vtable = comptime &.{

View file

@ -15,7 +15,7 @@ const debug_lsp = true;
const OutOfMemoryError = error{OutOfMemory}; const OutOfMemoryError = error{OutOfMemory};
const SendError = error{SendFailed}; const SendError = error{SendFailed};
const SpawnError = error{ThespianSpawnFailed}; const CallError = tp.CallError;
pub fn open(allocator: std.mem.Allocator, project: []const u8, cmd: tp.message) (error{ ThespianSpawnFailed, InvalidLspCommand } || cbor.Error)!Self { pub fn open(allocator: std.mem.Allocator, project: []const u8, cmd: tp.message) (error{ ThespianSpawnFailed, InvalidLspCommand } || cbor.Error)!Self {
return .{ .allocator = allocator, .pid = try Process.create(allocator, project, cmd) }; return .{ .allocator = allocator, .pid = try Process.create(allocator, project, cmd) };
@ -31,17 +31,12 @@ pub fn term(self: Self) void {
self.pid.deinit(); self.pid.deinit();
} }
pub fn send_request( pub fn send_request(self: Self, allocator: std.mem.Allocator, method: []const u8, m: anytype) CallError!tp.message {
self: Self,
allocator: std.mem.Allocator,
method: []const u8,
m: anytype,
ctx: anytype,
) (OutOfMemoryError || SpawnError)!void {
var cb = std.ArrayList(u8).init(self.allocator); var cb = std.ArrayList(u8).init(self.allocator);
defer cb.deinit(); defer cb.deinit();
try cbor.writeValue(cb.writer(), m); try cbor.writeValue(cb.writer(), m);
return RequestContext(@TypeOf(ctx)).send(allocator, self.pid.ref(), ctx, tp.message.fmt(.{ "REQ", method, cb.items })); const request_timeout: u64 = @intCast(std.time.ns_per_s * tp.env.get().num("lsp-request-timeout"));
return self.pid.call(allocator, request_timeout, .{ "REQ", method, cb.items });
} }
pub fn send_notification(self: Self, method: []const u8, m: anytype) (OutOfMemoryError || SendError)!void { pub fn send_notification(self: Self, method: []const u8, m: anytype) (OutOfMemoryError || SendError)!void {
@ -59,57 +54,6 @@ pub fn close(self: *Self) void {
self.deinit(); self.deinit();
} }
fn RequestContext(T: type) type {
return struct {
receiver: ReceiverT,
ctx: T,
to: tp.pid,
request: tp.message,
response: ?tp.message,
a: std.mem.Allocator,
const Self = @This();
const ReceiverT = tp.Receiver(*@This());
fn send(a: std.mem.Allocator, to: tp.pid_ref, ctx: T, request: tp.message) (OutOfMemoryError || SpawnError)!void {
const self = try a.create(@This());
self.* = .{
.receiver = undefined,
.ctx = if (@hasDecl(T, "clone")) ctx.clone() else ctx,
.to = to.clone(),
.request = try request.clone(std.heap.c_allocator),
.response = null,
.a = a,
};
self.receiver = ReceiverT.init(receive_, self);
const proc = try tp.spawn_link(a, self, start, @typeName(@This()));
defer proc.deinit();
}
fn deinit(self: *@This()) void {
self.ctx.deinit();
std.heap.c_allocator.free(self.request.buf);
self.to.deinit();
self.a.destroy(self);
}
fn start(self: *@This()) tp.result {
_ = tp.set_trap(true);
if (@hasDecl(T, "link")) try self.ctx.link();
errdefer self.deinit();
try self.to.link();
try self.to.send_raw(self.request);
tp.receive(&self.receiver);
}
fn receive_(self: *@This(), _: tp.pid_ref, m: tp.message) tp.result {
defer self.deinit();
self.ctx.receive(m) catch |e| return tp.exit_error(e, @errorReturnTrace());
return tp.exit_normal();
}
};
}
const Process = struct { const Process = struct {
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
cmd: tp.message, cmd: tp.message,
@ -123,10 +67,6 @@ const Process = struct {
log_file: ?std.fs.File = null, log_file: ?std.fs.File = null,
next_id: i32 = 0, next_id: i32 = 0,
requests: std.StringHashMap(tp.pid), requests: std.StringHashMap(tp.pid),
state: enum { init, running } = .init,
init_queue: ?std.ArrayListUnmanaged(struct { tp.pid, []const u8, []const u8, InitQueueType }) = null,
const InitQueueType = enum { request, notify };
const Receiver = tp.Receiver(*Process); const Receiver = tp.Receiver(*Process);
@ -163,7 +103,6 @@ const Process = struct {
} }
fn deinit(self: *Process) void { fn deinit(self: *Process) void {
self.free_init_queue();
var i = self.requests.iterator(); var i = self.requests.iterator();
while (i.next()) |req| { while (i.next()) |req| {
self.allocator.free(req.key_ptr.*); self.allocator.free(req.key_ptr.*);
@ -227,37 +166,24 @@ const Process = struct {
UnsupportedType, UnsupportedType,
ExitNormal, ExitNormal,
ExitUnexpected, ExitUnexpected,
InvalidMapType,
}); });
fn receive_safe(self: *Process, from: tp.pid_ref, m: tp.message) Error!void { fn receive_safe(self: *Process, from: tp.pid_ref, m: tp.message) Error!void {
const frame = tracy.initZone(@src(), .{ .name = module_name }); const frame = tracy.initZone(@src(), .{ .name = module_name });
defer frame.deinit(); defer frame.deinit();
errdefer self.deinit(); errdefer self.deinit();
var method: []const u8 = ""; var method: []u8 = "";
var bytes: []const u8 = ""; var bytes: []u8 = "";
var err: []const u8 = ""; var err: []u8 = "";
var code: u32 = 0; var code: u32 = 0;
var cbor_id: []const u8 = ""; var cbor_id: []const u8 = "";
if (try cbor.match(m.buf, .{ "REQ", "initialize", tp.extract(&bytes) })) { if (try cbor.match(m.buf, .{ "REQ", tp.extract(&method), tp.extract(&bytes) })) {
try self.send_request(from, "initialize", bytes); try self.send_request(from, method, bytes);
} else if (try cbor.match(m.buf, .{ "REQ", tp.extract(&method), tp.extract(&bytes) })) {
switch (self.state) {
.init => try self.append_init_queue(from, method, bytes, .request), //queue requests
.running => try self.send_request(from, method, bytes),
}
} else if (try cbor.match(m.buf, .{ "RSP", tp.extract_cbor(&cbor_id), tp.extract_cbor(&bytes) })) { } else if (try cbor.match(m.buf, .{ "RSP", tp.extract_cbor(&cbor_id), tp.extract_cbor(&bytes) })) {
try self.send_response(cbor_id, bytes); try self.send_response(cbor_id, bytes);
} else if (try cbor.match(m.buf, .{ "NTFY", "initialized", tp.extract(&bytes) })) {
self.state = .running;
try self.send_notification("initialized", bytes);
try self.replay_init_queue();
} else if (try cbor.match(m.buf, .{ "NTFY", tp.extract(&method), tp.extract(&bytes) })) { } else if (try cbor.match(m.buf, .{ "NTFY", tp.extract(&method), tp.extract(&bytes) })) {
switch (self.state) { try self.send_notification(method, bytes);
.init => try self.append_init_queue(from, method, bytes, .notify), //queue requests
.running => try self.send_notification(method, bytes),
}
} else if (try cbor.match(m.buf, .{"close"})) { } else if (try cbor.match(m.buf, .{"close"})) {
self.write_log("### LSP close ###\n", .{}); self.write_log("### LSP close ###\n", .{});
try self.close(); try self.close();
@ -281,43 +207,6 @@ const Process = struct {
} }
} }
fn append_init_queue(self: *Process, from: tp.pid_ref, method: []const u8, bytes: []const u8, type_: InitQueueType) !void {
const queue = if (self.init_queue) |*queue| queue else blk: {
self.init_queue = .empty;
break :blk &self.init_queue.?;
};
const p = try queue.addOne(self.allocator);
p.* = .{
from.clone(),
try self.allocator.dupe(u8, method),
try self.allocator.dupe(u8, bytes),
type_,
};
}
fn replay_init_queue(self: *Process) !void {
defer self.free_init_queue();
if (self.init_queue) |*queue| {
for (queue.items) |*p|
switch (p[3]) {
.request => try self.send_request(p[0].ref(), p[1], p[2]),
.notify => try self.send_notification(p[1], p[2]),
};
}
}
fn free_init_queue(self: *Process) void {
if (self.init_queue) |*queue| {
for (queue.items) |*p| {
p[0].deinit();
self.allocator.free(p[1]);
self.allocator.free(p[2]);
}
queue.deinit(self.allocator);
}
self.init_queue = null;
}
fn receive_lsp_message(self: *Process, cb: []const u8) Error!void { fn receive_lsp_message(self: *Process, cb: []const u8) Error!void {
var iter = cb; var iter = cb;
@ -370,7 +259,7 @@ const Process = struct {
} }
} }
fn handle_output(self: *Process, bytes: []const u8) Error!void { fn handle_output(self: *Process, bytes: []u8) Error!void {
try self.recv_buf.appendSlice(bytes); try self.recv_buf.appendSlice(bytes);
self.write_log("### RECV:\n{s}\n###\n", .{bytes}); self.write_log("### RECV:\n{s}\n###\n", .{bytes});
self.frame_message_recv() catch |e| { self.frame_message_recv() catch |e| {

View file

@ -7,7 +7,6 @@ const dizzy = @import("dizzy");
const Buffer = @import("Buffer"); const Buffer = @import("Buffer");
const fuzzig = @import("fuzzig"); const fuzzig = @import("fuzzig");
const tracy = @import("tracy"); const tracy = @import("tracy");
const git = @import("git");
const builtin = @import("builtin"); const builtin = @import("builtin");
const LSP = @import("LSP.zig"); const LSP = @import("LSP.zig");
@ -22,17 +21,13 @@ language_servers: std.StringHashMap(LSP),
file_language_server: std.StringHashMap(LSP), file_language_server: std.StringHashMap(LSP),
tasks: std.ArrayList(Task), tasks: std.ArrayList(Task),
persistent: bool = false, persistent: bool = false,
logger_lsp: log.Logger,
logger_git: log.Logger,
workspace: ?[]const u8 = null,
branch: ?[]const u8 = null,
const Self = @This(); const Self = @This();
const OutOfMemoryError = error{OutOfMemory}; const OutOfMemoryError = error{OutOfMemory};
const CallError = tp.CallError;
const SpawnError = (OutOfMemoryError || error{ThespianSpawnFailed}); const SpawnError = (OutOfMemoryError || error{ThespianSpawnFailed});
pub const InvalidMessageError = error{ InvalidMessage, InvalidMessageField, InvalidTargetURI, InvalidMapType }; pub const InvalidMessageError = error{ InvalidMessage, InvalidMessageField, InvalidTargetURI };
pub const StartLspError = (error{ ThespianSpawnFailed, Timeout, InvalidLspCommand } || LspError || OutOfMemoryError || cbor.Error); pub const StartLspError = (error{ ThespianSpawnFailed, Timeout, InvalidLspCommand } || LspError || OutOfMemoryError || cbor.Error);
pub const LspError = (error{ NoLsp, LspFailed } || OutOfMemoryError); pub const LspError = (error{ NoLsp, LspFailed } || OutOfMemoryError);
pub const ClientError = (error{ClientFailed} || OutOfMemoryError); pub const ClientError = (error{ClientFailed} || OutOfMemoryError);
@ -65,14 +60,10 @@ pub fn init(allocator: std.mem.Allocator, name: []const u8) OutOfMemoryError!Sel
.language_servers = std.StringHashMap(LSP).init(allocator), .language_servers = std.StringHashMap(LSP).init(allocator),
.file_language_server = std.StringHashMap(LSP).init(allocator), .file_language_server = std.StringHashMap(LSP).init(allocator),
.tasks = std.ArrayList(Task).init(allocator), .tasks = std.ArrayList(Task).init(allocator),
.logger_lsp = log.logger("lsp"),
.logger_git = log.logger("git"),
}; };
} }
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
if (self.workspace) |p| self.allocator.free(p);
if (self.branch) |p| self.allocator.free(p);
var i_ = self.file_language_server.iterator(); var i_ = self.file_language_server.iterator();
while (i_.next()) |p| { while (i_.next()) |p| {
self.allocator.free(p.key_ptr.*); self.allocator.free(p.key_ptr.*);
@ -86,8 +77,6 @@ pub fn deinit(self: *Self) void {
self.files.deinit(); self.files.deinit();
for (self.tasks.items) |task| self.allocator.free(task.command); for (self.tasks.items) |task| self.allocator.free(task.command);
self.tasks.deinit(); self.tasks.deinit();
self.logger_lsp.deinit();
self.logger_git.deinit();
self.allocator.free(self.name); self.allocator.free(self.name);
} }
@ -143,7 +132,7 @@ pub fn restore_state(self: *Self, data: []const u8) !void {
var iter: []const u8 = data; var iter: []const u8 = data;
_ = cbor.matchValue(&iter, tp.string) catch {}; _ = cbor.matchValue(&iter, tp.string) catch {};
_ = cbor.decodeArrayHeader(&iter) catch |e| switch (e) { _ = cbor.decodeArrayHeader(&iter) catch |e| switch (e) {
error.InvalidArrayType => return self.restore_state_v0(data), error.InvalidType => return self.restore_state_v0(data),
else => return tp.trace(tp.channel.debug, .{ "restore_state", "unknown format", data }), else => return tp.trace(tp.channel.debug, .{ "restore_state", "unknown format", data }),
}; };
self.persistent = true; self.persistent = true;
@ -203,18 +192,7 @@ pub fn restore_state_v1(self: *Self, data: []const u8) !void {
} }
} }
pub fn restore_state_v0(self: *Self, data: []const u8) error{ pub fn restore_state_v0(self: *Self, data: []const u8) error{ OutOfMemory, IntegerTooLarge, IntegerTooSmall, InvalidType, TooShort }!void {
OutOfMemory,
IntegerTooLarge,
IntegerTooSmall,
InvalidType,
TooShort,
InvalidFloatType,
InvalidArrayType,
InvalidPIntType,
JsonIncompatibleType,
NotAnObject,
}!void {
tp.trace(tp.channel.debug, .{"restore_state_v0"}); tp.trace(tp.channel.debug, .{"restore_state_v0"});
defer self.sort_files_by_mtime(); defer self.sort_files_by_mtime();
var name: []const u8 = undefined; var name: []const u8 = undefined;
@ -256,8 +234,11 @@ fn get_language_server_instance(self: *Self, language_server: []const u8) StartL
defer self.allocator.free(uri); defer self.allocator.free(uri);
const basename_begin = std.mem.lastIndexOfScalar(u8, self.name, std.fs.path.sep); const basename_begin = std.mem.lastIndexOfScalar(u8, self.name, std.fs.path.sep);
const basename = if (basename_begin) |begin| self.name[begin + 1 ..] else self.name; const basename = if (basename_begin) |begin| self.name[begin + 1 ..] else self.name;
const response = try self.send_lsp_init_request(lsp, self.name, basename, uri);
try self.send_lsp_init_request(lsp, self.name, basename, uri, language_server); defer self.allocator.free(response.buf);
lsp.send_notification("initialized", .{}) catch return error.LspFailed;
if (lsp.pid.expired()) return error.LspFailed;
log.logger("lsp").print("initialized LSP: {s}", .{fmt_lsp_name_func(language_server)});
try self.language_servers.put(try self.allocator.dupe(u8, language_server), lsp); try self.language_servers.put(try self.allocator.dupe(u8, language_server), lsp);
return lsp; return lsp;
} }
@ -639,45 +620,27 @@ fn send_goto_request(self: *Self, from: tp.pid_ref, file_path: []const u8, row:
const lsp = try self.get_language_server(file_path); const lsp = try self.get_language_server(file_path);
const uri = try self.make_URI(file_path); const uri = try self.make_URI(file_path);
defer self.allocator.free(uri); defer self.allocator.free(uri);
const response = lsp.send_request(self.allocator, method, .{
const handler: struct {
from: tp.pid,
name: []const u8,
project: *Self,
pub fn deinit(self_: *@This()) void {
std.heap.c_allocator.free(self_.name);
self_.from.deinit();
}
pub fn receive(self_: @This(), response: tp.message) !void {
var link: []const u8 = undefined;
var locations: []const u8 = undefined;
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.array })) {
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, .{tp.extract_cbor(&link)} })) {
try navigate_to_location_link(self_.from.ref(), link);
} else if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&locations) })) {
try self_.project.send_reference_list(self_.from.ref(), locations, self_.name);
}
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
return;
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&link) })) {
try navigate_to_location_link(self_.from.ref(), link);
}
}
} = .{
.from = from.clone(),
.name = try std.heap.c_allocator.dupe(u8, self.name),
.project = self,
};
lsp.send_request(self.allocator, method, .{
.textDocument = .{ .uri = uri }, .textDocument = .{ .uri = uri },
.position = .{ .line = row, .character = col }, .position = .{ .line = row, .character = col },
}, handler) catch return error.LspFailed; }) catch return error.LspFailed;
defer self.allocator.free(response.buf);
var link: []const u8 = undefined;
var locations: []const u8 = undefined;
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.array })) {
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, .{tp.extract_cbor(&link)} })) {
try self.navigate_to_location_link(from, link);
} else if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&locations) })) {
try self.send_reference_list(from, locations);
}
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
return;
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&link) })) {
try self.navigate_to_location_link(from, link);
}
} }
fn navigate_to_location_link(from: tp.pid_ref, location_link: []const u8) (ClientError || InvalidMessageError || cbor.Error)!void { fn navigate_to_location_link(_: *Self, from: tp.pid_ref, location_link: []const u8) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = location_link; var iter = location_link;
var targetUri: ?[]const u8 = null; var targetUri: ?[]const u8 = null;
var targetRange: ?Range = null; var targetRange: ?Range = null;
@ -739,40 +702,23 @@ pub fn references(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi
const lsp = try self.get_language_server(file_path); const lsp = try self.get_language_server(file_path);
const uri = try self.make_URI(file_path); const uri = try self.make_URI(file_path);
defer self.allocator.free(uri); defer self.allocator.free(uri);
self.logger_lsp.print("finding references...", .{}); log.logger("lsp").print("finding references...", .{});
const handler: struct { const response = lsp.send_request(self.allocator, "textDocument/references", .{
from: tp.pid,
name: []const u8,
project: *Self,
pub fn deinit(self_: *@This()) void {
std.heap.c_allocator.free(self_.name);
self_.from.deinit();
}
pub fn receive(self_: @This(), response: tp.message) !void {
var locations: []const u8 = undefined;
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
return;
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&locations) })) {
try self_.project.send_reference_list(self_.from.ref(), locations, self_.name);
}
}
} = .{
.from = from.clone(),
.name = try std.heap.c_allocator.dupe(u8, self.name),
.project = self,
};
lsp.send_request(self.allocator, "textDocument/references", .{
.textDocument = .{ .uri = uri }, .textDocument = .{ .uri = uri },
.position = .{ .line = row, .character = col }, .position = .{ .line = row, .character = col },
.context = .{ .includeDeclaration = true }, .context = .{ .includeDeclaration = true },
}, handler) catch return error.LspFailed; }) catch return error.LspFailed;
defer self.allocator.free(response.buf);
var locations: []const u8 = undefined;
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
return;
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&locations) })) {
try self.send_reference_list(from, locations);
}
} }
fn send_reference_list(self: *Self, to: tp.pid_ref, locations: []const u8, name: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void { fn send_reference_list(self: *Self, to: tp.pid_ref, locations: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void {
defer to.send(.{ "REF", "done" }) catch {}; defer to.send(.{ "REF", "done" }) catch {};
var iter = locations; var iter = locations;
var len = try cbor.decodeArrayHeader(&iter); var len = try cbor.decodeArrayHeader(&iter);
@ -780,14 +726,13 @@ fn send_reference_list(self: *Self, to: tp.pid_ref, locations: []const u8, name:
while (len > 0) : (len -= 1) { while (len > 0) : (len -= 1) {
var location: []const u8 = undefined; var location: []const u8 = undefined;
if (try cbor.matchValue(&iter, cbor.extract_cbor(&location))) { if (try cbor.matchValue(&iter, cbor.extract_cbor(&location))) {
try send_reference(to, location, name); try self.send_reference(to, location);
} else return error.InvalidMessageField; } else return error.InvalidMessageField;
} }
self.logger_lsp.print("found {d} references", .{count}); log.logger("lsp").print("found {d} references", .{count});
} }
fn send_reference(to: tp.pid_ref, location: []const u8, name: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void { fn send_reference(self: *Self, to: tp.pid_ref, location: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void {
const allocator = std.heap.c_allocator;
var iter = location; var iter = location;
var targetUri: ?[]const u8 = null; var targetUri: ?[]const u8 = null;
var targetRange: ?Range = null; var targetRange: ?Range = null;
@ -822,10 +767,10 @@ fn send_reference(to: tp.pid_ref, location: []const u8, name: []const u8) (Clien
file_path[i] = '\\'; file_path[i] = '\\';
}; };
} }
const line = try get_line_of_file(allocator, file_path, targetRange.?.start.line); const line = try self.get_line_of_file(self.allocator, file_path, targetRange.?.start.line);
defer allocator.free(line); defer self.allocator.free(line);
const file_path_ = if (file_path.len > name.len and std.mem.eql(u8, name, file_path[0..name.len])) const file_path_ = if (file_path.len > self.name.len and std.mem.eql(u8, self.name, file_path[0..self.name.len]))
file_path[name.len + 1 ..] file_path[self.name.len + 1 ..]
else else
file_path; file_path;
to.send(.{ to.send(.{
@ -843,44 +788,24 @@ pub fn completion(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usi
const lsp = try self.get_language_server(file_path); const lsp = try self.get_language_server(file_path);
const uri = try self.make_URI(file_path); const uri = try self.make_URI(file_path);
defer self.allocator.free(uri); defer self.allocator.free(uri);
const response = lsp.send_request(self.allocator, "textDocument/completion", .{
const handler: struct {
from: tp.pid,
file_path: []const u8,
row: usize,
col: usize,
pub fn deinit(self_: *@This()) void {
std.heap.c_allocator.free(self_.file_path);
self_.from.deinit();
}
pub fn receive(self_: @This(), response: tp.message) !void {
var result: []const u8 = undefined;
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
try send_content_msg_empty(self_.from.ref(), "hover", self_.file_path, self_.row, self_.col);
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.array })) {
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&result) }))
try send_completion_items(self_.from.ref(), self_.file_path, self_.row, self_.col, result, false);
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.map })) {
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&result) }))
try send_completion_list(self_.from.ref(), self_.file_path, self_.row, self_.col, result);
}
}
} = .{
.from = from.clone(),
.file_path = try std.heap.c_allocator.dupe(u8, file_path),
.row = row,
.col = col,
};
lsp.send_request(self.allocator, "textDocument/completion", .{
.textDocument = .{ .uri = uri }, .textDocument = .{ .uri = uri },
.position = .{ .line = row, .character = col }, .position = .{ .line = row, .character = col },
}, handler) catch return error.LspFailed; }) catch return error.LspFailed;
defer self.allocator.free(response.buf);
var result: []const u8 = undefined;
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
try send_content_msg_empty(from, "hover", file_path, row, col);
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.array })) {
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&result) }))
try self.send_completion_items(from, file_path, row, col, result, false);
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.map })) {
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&result) }))
try self.send_completion_list(from, file_path, row, col, result);
}
} }
fn send_completion_list(to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, result: []const u8) (ClientError || InvalidMessageError || cbor.Error)!void { fn send_completion_list(self: *Self, to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, result: []const u8) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = result; var iter = result;
var len = cbor.decodeMapHeader(&iter) catch return; var len = cbor.decodeMapHeader(&iter) catch return;
var items: []const u8 = ""; var items: []const u8 = "";
@ -896,20 +821,20 @@ fn send_completion_list(to: tp.pid_ref, file_path: []const u8, row: usize, col:
try cbor.skipValue(&iter); try cbor.skipValue(&iter);
} }
} }
return send_completion_items(to, file_path, row, col, items, is_incomplete) catch error.ClientFailed; return self.send_completion_items(to, file_path, row, col, items, is_incomplete) catch error.ClientFailed;
} }
fn send_completion_items(to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, items: []const u8, is_incomplete: bool) (ClientError || InvalidMessageError || cbor.Error)!void { fn send_completion_items(self: *Self, to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, items: []const u8, is_incomplete: bool) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = items; var iter = items;
var len = cbor.decodeArrayHeader(&iter) catch return; var len = cbor.decodeArrayHeader(&iter) catch return;
var item: []const u8 = ""; var item: []const u8 = "";
while (len > 0) : (len -= 1) { while (len > 0) : (len -= 1) {
if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&item)))) return error.InvalidMessageField; if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&item)))) return error.InvalidMessageField;
send_completion_item(to, file_path, row, col, item, if (len > 1) true else is_incomplete) catch return error.ClientFailed; self.send_completion_item(to, file_path, row, col, item, if (len > 1) true else is_incomplete) catch return error.ClientFailed;
} }
} }
fn send_completion_item(to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, item: []const u8, is_incomplete: bool) (ClientError || InvalidMessageError || cbor.Error)!void { fn send_completion_item(_: *Self, to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, item: []const u8, is_incomplete: bool) (ClientError || InvalidMessageError || cbor.Error)!void {
var label: []const u8 = ""; var label: []const u8 = "";
var label_detail: []const u8 = ""; var label_detail: []const u8 = "";
var label_description: []const u8 = ""; var label_description: []const u8 = "";
@ -1023,74 +948,57 @@ pub fn rename_symbol(self: *Self, from: tp.pid_ref, file_path: []const u8, row:
const lsp = try self.get_language_server(file_path); const lsp = try self.get_language_server(file_path);
const uri = try self.make_URI(file_path); const uri = try self.make_URI(file_path);
defer self.allocator.free(uri); defer self.allocator.free(uri);
const response = lsp.send_request(self.allocator, "textDocument/rename", .{
const handler: struct {
from: tp.pid,
file_path: []const u8,
pub fn deinit(self_: *@This()) void {
std.heap.c_allocator.free(self_.file_path);
self_.from.deinit();
}
pub fn receive(self_: @This(), response: tp.message) !void {
const allocator = std.heap.c_allocator;
var result: []const u8 = undefined;
// buffer the renames in order to send as a single, atomic message
var renames = std.ArrayList(Rename).init(allocator);
defer renames.deinit();
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.map })) {
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&result) })) {
try decode_rename_symbol_map(result, &renames);
// write the renames message manually since there doesn't appear to be an array helper
var msg_buf = std.ArrayList(u8).init(allocator);
defer msg_buf.deinit();
const w = msg_buf.writer();
try cbor.writeArrayHeader(w, 3);
try cbor.writeValue(w, "cmd");
try cbor.writeValue(w, "rename_symbol_item");
try cbor.writeArrayHeader(w, renames.items.len);
for (renames.items) |rename| {
if (!std.mem.eql(u8, rename.uri[0..7], "file://")) return error.InvalidTargetURI;
var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
var file_path_ = std.Uri.percentDecodeBackwards(&file_path_buf, rename.uri[7..]);
if (builtin.os.tag == .windows) {
if (file_path_[0] == '/') file_path_ = file_path_[1..];
for (file_path_, 0..) |c, i| if (c == '/') {
file_path_[i] = '\\';
};
}
const line = try get_line_of_file(allocator, self_.file_path, rename.range.start.line);
try cbor.writeValue(w, .{
file_path_,
rename.range.start.line,
rename.range.start.character,
rename.range.end.line,
rename.range.end.character,
rename.new_text,
line,
});
}
self_.from.send_raw(.{ .buf = msg_buf.items }) catch return error.ClientFailed;
}
}
}
} = .{
.from = from.clone(),
.file_path = try std.heap.c_allocator.dupe(u8, file_path),
};
lsp.send_request(self.allocator, "textDocument/rename", .{
.textDocument = .{ .uri = uri }, .textDocument = .{ .uri = uri },
.position = .{ .line = row, .character = col }, .position = .{ .line = row, .character = col },
.newName = "PLACEHOLDER", .newName = "PLACEHOLDER",
}, handler) catch return error.LspFailed; }) catch return error.LspFailed;
defer self.allocator.free(response.buf);
var result: []const u8 = undefined;
// buffer the renames in order to send as a single, atomic message
var renames = std.ArrayList(Rename).init(self.allocator);
defer renames.deinit();
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.map })) {
if (try cbor.match(response.buf, .{ tp.any, tp.any, tp.any, tp.extract_cbor(&result) })) {
try self.decode_rename_symbol_map(result, &renames);
// write the renames message manually since there doesn't appear to be an array helper
var msg_buf = std.ArrayList(u8).init(self.allocator);
defer msg_buf.deinit();
const w = msg_buf.writer();
try cbor.writeArrayHeader(w, 3);
try cbor.writeValue(w, "cmd");
try cbor.writeValue(w, "rename_symbol_item");
try cbor.writeArrayHeader(w, renames.items.len);
for (renames.items) |rename| {
if (!std.mem.eql(u8, rename.uri[0..7], "file://")) return error.InvalidTargetURI;
var file_path_buf: [std.fs.max_path_bytes]u8 = undefined;
var file_path_ = std.Uri.percentDecodeBackwards(&file_path_buf, rename.uri[7..]);
if (builtin.os.tag == .windows) {
if (file_path_[0] == '/') file_path_ = file_path_[1..];
for (file_path_, 0..) |c, i| if (c == '/') {
file_path_[i] = '\\';
};
}
const line = try self.get_line_of_file(self.allocator, file_path, rename.range.start.line);
try cbor.writeValue(w, .{
file_path_,
rename.range.start.line,
rename.range.start.character,
rename.range.end.line,
rename.range.end.character,
rename.new_text,
line,
});
}
from.send_raw(.{ .buf = msg_buf.items }) catch return error.ClientFailed;
}
}
} }
// decode a WorkspaceEdit record which may have shape {"changes": {}} or {"documentChanges": []} // decode a WorkspaceEdit record which may have shape {"changes": {}} or {"documentChanges": []}
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspaceEdit
fn decode_rename_symbol_map(result: []const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void { fn decode_rename_symbol_map(self: *Self, result: []const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = result; var iter = result;
var len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage; var len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage;
var changes: []const u8 = ""; var changes: []const u8 = "";
@ -1099,11 +1007,11 @@ fn decode_rename_symbol_map(result: []const u8, renames: *std.ArrayList(Rename))
if (!(try cbor.matchString(&iter, &field_name))) return error.InvalidMessage; if (!(try cbor.matchString(&iter, &field_name))) return error.InvalidMessage;
if (std.mem.eql(u8, field_name, "changes")) { if (std.mem.eql(u8, field_name, "changes")) {
if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&changes)))) return error.InvalidMessageField; if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&changes)))) return error.InvalidMessageField;
try decode_rename_symbol_changes(changes, renames); try self.decode_rename_symbol_changes(changes, renames);
return; return;
} else if (std.mem.eql(u8, field_name, "documentChanges")) { } else if (std.mem.eql(u8, field_name, "documentChanges")) {
if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&changes)))) return error.InvalidMessageField; if (!(try cbor.matchValue(&iter, cbor.extract_cbor(&changes)))) return error.InvalidMessageField;
try decode_rename_symbol_doc_changes(changes, renames); try self.decode_rename_symbol_doc_changes(changes, renames);
return; return;
} else { } else {
try cbor.skipValue(&iter); try cbor.skipValue(&iter);
@ -1112,17 +1020,17 @@ fn decode_rename_symbol_map(result: []const u8, renames: *std.ArrayList(Rename))
return error.ClientFailed; return error.ClientFailed;
} }
fn decode_rename_symbol_changes(changes: []const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void { fn decode_rename_symbol_changes(self: *Self, changes: []const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = changes; var iter = changes;
var files_len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage; var files_len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage;
while (files_len > 0) : (files_len -= 1) { while (files_len > 0) : (files_len -= 1) {
var file_uri: []const u8 = undefined; var file_uri: []const u8 = undefined;
if (!(try cbor.matchString(&iter, &file_uri))) return error.InvalidMessage; if (!(try cbor.matchString(&iter, &file_uri))) return error.InvalidMessage;
try decode_rename_symbol_item(file_uri, &iter, renames); try decode_rename_symbol_item(self, file_uri, &iter, renames);
} }
} }
fn decode_rename_symbol_doc_changes(changes: []const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void { fn decode_rename_symbol_doc_changes(self: *Self, changes: []const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = changes; var iter = changes;
var changes_len = cbor.decodeArrayHeader(&iter) catch return error.InvalidMessage; var changes_len = cbor.decodeArrayHeader(&iter) catch return error.InvalidMessage;
while (changes_len > 0) : (changes_len -= 1) { while (changes_len > 0) : (changes_len -= 1) {
@ -1142,14 +1050,14 @@ fn decode_rename_symbol_doc_changes(changes: []const u8, renames: *std.ArrayList
} }
} else if (std.mem.eql(u8, field_name, "edits")) { } else if (std.mem.eql(u8, field_name, "edits")) {
if (file_uri.len == 0) return error.InvalidMessage; if (file_uri.len == 0) return error.InvalidMessage;
try decode_rename_symbol_item(file_uri, &iter, renames); try decode_rename_symbol_item(self, file_uri, &iter, renames);
} }
} }
} }
} }
// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textEdit
fn decode_rename_symbol_item(file_uri: []const u8, iter: *[]const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void { fn decode_rename_symbol_item(_: *Self, file_uri: []const u8, iter: *[]const u8, renames: *std.ArrayList(Rename)) (ClientError || InvalidMessageError || cbor.Error)!void {
var text_edits_len = cbor.decodeArrayHeader(iter) catch return error.InvalidMessage; var text_edits_len = cbor.decodeArrayHeader(iter) catch return error.InvalidMessage;
while (text_edits_len > 0) : (text_edits_len -= 1) { while (text_edits_len > 0) : (text_edits_len -= 1) {
var m_range: ?Range = null; var m_range: ?Range = null;
@ -1178,41 +1086,22 @@ pub fn hover(self: *Self, from: tp.pid_ref, file_path: []const u8, row: usize, c
const lsp = try self.get_language_server(file_path); const lsp = try self.get_language_server(file_path);
const uri = try self.make_URI(file_path); const uri = try self.make_URI(file_path);
defer self.allocator.free(uri); defer self.allocator.free(uri);
// self.logger_lsp.print("fetching hover information...", .{}); // log.logger("lsp").print("fetching hover information...", .{});
const handler: struct { const response = lsp.send_request(self.allocator, "textDocument/hover", .{
from: tp.pid,
file_path: []const u8,
row: usize,
col: usize,
pub fn deinit(self_: *@This()) void {
self_.from.deinit();
std.heap.c_allocator.free(self_.file_path);
}
pub fn receive(self_: @This(), response: tp.message) !void {
var result: []const u8 = undefined;
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
try send_content_msg_empty(self_.from.ref(), "hover", self_.file_path, self_.row, self_.col);
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&result) })) {
try send_hover(self_.from.ref(), self_.file_path, self_.row, self_.col, result);
}
}
} = .{
.from = from.clone(),
.file_path = try std.heap.c_allocator.dupe(u8, file_path),
.row = row,
.col = col,
};
lsp.send_request(self.allocator, "textDocument/hover", .{
.textDocument = .{ .uri = uri }, .textDocument = .{ .uri = uri },
.position = .{ .line = row, .character = col }, .position = .{ .line = row, .character = col },
}, handler) catch return error.LspFailed; }) catch return error.LspFailed;
defer self.allocator.free(response.buf);
var result: []const u8 = undefined;
if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.null_ })) {
try send_content_msg_empty(from, "hover", file_path, row, col);
} else if (try cbor.match(response.buf, .{ "child", tp.string, "result", tp.extract_cbor(&result) })) {
try self.send_hover(from, file_path, row, col, result);
}
} }
fn send_hover(to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, result: []const u8) (ClientError || InvalidMessageError || cbor.Error)!void { fn send_hover(self: *Self, to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, result: []const u8) (ClientError || InvalidMessageError || cbor.Error)!void {
var iter = result; var iter = result;
var len = cbor.decodeMapHeader(&iter) catch return; var len = cbor.decodeMapHeader(&iter) catch return;
var contents: []const u8 = ""; var contents: []const u8 = "";
@ -1231,10 +1120,11 @@ fn send_hover(to: tp.pid_ref, file_path: []const u8, row: usize, col: usize, res
} }
} }
if (contents.len > 0) if (contents.len > 0)
return send_contents(to, "hover", file_path, row, col, contents, range); return self.send_contents(to, "hover", file_path, row, col, contents, range);
} }
fn send_contents( fn send_contents(
self: *Self,
to: tp.pid_ref, to: tp.pid_ref,
tag: []const u8, tag: []const u8,
file_path: []const u8, file_path: []const u8,
@ -1257,7 +1147,7 @@ fn send_contents(
}; };
if (is_list) { if (is_list) {
var content = std.ArrayList(u8).init(std.heap.c_allocator); var content = std.ArrayList(u8).init(self.allocator);
defer content.deinit(); defer content.deinit();
while (len > 0) : (len -= 1) { while (len > 0) : (len -= 1) {
if (try cbor.matchValue(&iter, cbor.extract(&value))) { if (try cbor.matchValue(&iter, cbor.extract(&value))) {
@ -1428,7 +1318,7 @@ fn read_position(position: []const u8) !Position {
return .{ .line = line.?, .character = character.? }; return .{ .line = line.?, .character = character.? };
} }
pub fn show_message(self: *Self, _: tp.pid_ref, params_cb: []const u8) !void { pub fn show_message(_: *Self, _: tp.pid_ref, params_cb: []const u8) !void {
var type_: i32 = 0; var type_: i32 = 0;
var message: ?[]const u8 = null; var message: ?[]const u8 = null;
var iter = params_cb; var iter = params_cb;
@ -1445,10 +1335,12 @@ pub fn show_message(self: *Self, _: tp.pid_ref, params_cb: []const u8) !void {
} }
} }
const msg = message orelse return; const msg = message orelse return;
const logger = log.logger("lsp");
defer logger.deinit();
if (type_ <= 2) if (type_ <= 2)
self.logger_lsp.err_msg("lsp", msg) logger.err_msg("lsp", msg)
else else
self.logger_lsp.print("{s}", .{msg}); logger.print("{s}", .{msg});
} }
pub fn register_capability(self: *Self, from: tp.pid_ref, cbor_id: []const u8, params_cb: []const u8) ClientError!void { pub fn register_capability(self: *Self, from: tp.pid_ref, cbor_id: []const u8, params_cb: []const u8) ClientError!void {
@ -1472,32 +1364,8 @@ pub fn send_lsp_response(self: *Self, from: tp.pid_ref, cbor_id: []const u8, res
from.send_raw(.{ .buf = cb.items }) catch return error.ClientFailed; from.send_raw(.{ .buf = cb.items }) catch return error.ClientFailed;
} }
fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, project_basename: []const u8, project_uri: []const u8, language_server: []const u8) !void { fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, project_basename: []const u8, project_uri: []const u8) CallError!tp.message {
const handler: struct { return lsp.send_request(self.allocator, "initialize", .{
language_server: []const u8,
lsp: LSP,
project: *Self,
pub fn deinit(self_: *@This()) void {
self_.lsp.pid.deinit();
std.heap.c_allocator.free(self_.language_server);
}
pub fn receive(self_: @This(), _: tp.message) !void {
self_.lsp.send_notification("initialized", .{}) catch return error.LspFailed;
if (self_.lsp.pid.expired()) return error.LspFailed;
self_.project.logger_lsp.print("initialized LSP: {s}", .{fmt_lsp_name_func(self_.language_server)});
}
} = .{
.language_server = try std.heap.c_allocator.dupe(u8, language_server),
.lsp = .{
.allocator = lsp.allocator,
.pid = lsp.pid.clone(),
},
.project = self,
};
try lsp.send_request(self.allocator, "initialize", .{
.processId = if (builtin.os.tag == .linux) std.os.linux.getpid() else null, .processId = if (builtin.os.tag == .linux) std.os.linux.getpid() else null,
.rootPath = project_path, .rootPath = project_path,
.rootUri = project_uri, .rootUri = project_uri,
@ -1799,7 +1667,7 @@ fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, projec
}, },
}, },
}, },
}, handler); });
} }
fn fmt_lsp_name_func(bytes: []const u8) std.fmt.Formatter(format_lsp_name_func) { fn fmt_lsp_name_func(bytes: []const u8) std.fmt.Formatter(format_lsp_name_func) {
@ -1830,7 +1698,7 @@ const eol = '\n';
pub const GetLineOfFileError = (OutOfMemoryError || std.fs.File.OpenError || std.fs.File.Reader.Error); pub const GetLineOfFileError = (OutOfMemoryError || std.fs.File.OpenError || std.fs.File.Reader.Error);
fn get_line_of_file(allocator: std.mem.Allocator, file_path: []const u8, line_: usize) GetLineOfFileError![]const u8 { fn get_line_of_file(self: *Self, allocator: std.mem.Allocator, file_path: []const u8, line_: usize) GetLineOfFileError![]const u8 {
const line = line_ + 1; const line = line_ + 1;
const file = try std.fs.cwd().openFile(file_path, .{ .mode = .read_only }); const file = try std.fs.cwd().openFile(file_path, .{ .mode = .read_only });
defer file.close(); defer file.close();
@ -1844,38 +1712,15 @@ fn get_line_of_file(allocator: std.mem.Allocator, file_path: []const u8, line_:
var line_count: usize = 1; var line_count: usize = 1;
for (0..buf.len) |i| { for (0..buf.len) |i| {
if (line_count == line) if (line_count == line)
return get_line(allocator, buf[i..]); return self.get_line(allocator, buf[i..]);
if (buf[i] == eol) line_count += 1; if (buf[i] == eol) line_count += 1;
} }
return allocator.dupe(u8, ""); return allocator.dupe(u8, "");
} }
pub fn get_line(allocator: std.mem.Allocator, buf: []const u8) ![]const u8 { pub fn get_line(_: *Self, allocator: std.mem.Allocator, buf: []const u8) ![]const u8 {
for (0..buf.len) |i| { for (0..buf.len) |i| {
if (buf[i] == eol) return allocator.dupe(u8, buf[0..i]); if (buf[i] == eol) return allocator.dupe(u8, buf[0..i]);
} }
return allocator.dupe(u8, buf); return allocator.dupe(u8, buf);
} }
pub fn query_git(self: *Self) void {
git.workspace_path(@intFromPtr(self)) catch {};
git.current_branch(@intFromPtr(self)) catch {};
}
pub fn process_git(self: *Self, m: tp.message) !void {
var value: []const u8 = undefined;
if (try m.match(.{ tp.any, tp.any, "workspace_path", tp.null_ })) {
// no git workspace
} else if (try m.match(.{ tp.any, tp.any, "workspace_path", tp.extract(&value) })) {
if (self.workspace) |p| self.allocator.free(p);
self.workspace = try self.allocator.dupe(u8, value);
git.workspace_files(@intFromPtr(self)) catch {};
} else if (try m.match(.{ tp.any, tp.any, "current_branch", tp.extract(&value) })) {
if (self.branch) |p| self.allocator.free(p);
self.branch = try self.allocator.dupe(u8, value);
} else if (try m.match(.{ tp.any, tp.any, "workspace_files", tp.extract(&value) })) {
// TODO
} else {
self.logger_git.err("git", tp.unexpected(m));
}
}

View file

@ -55,7 +55,7 @@ file_type_icon: ?[]const u8 = null,
file_type_color: ?u24 = null, file_type_color: ?u24 = null,
pub const EolMode = enum { lf, crlf }; pub const EolMode = enum { lf, crlf };
pub const EolModeTag = @typeInfo(EolMode).@"enum".tag_type; pub const EolModeTag = @typeInfo(EolMode).Enum.tag_type;
const UndoNode = struct { const UndoNode = struct {
root: Root, root: Root,
@ -71,26 +71,26 @@ const UndoBranch = struct {
}; };
pub const WalkerMut = struct { pub const WalkerMut = struct {
keep_walking_: bool = false, keep_walking: bool = false,
found_: bool = false, found: bool = false,
replace: ?Root = null, replace: ?Root = null,
err: ?anyerror = null, err: ?anyerror = null,
pub const keep_walking = WalkerMut{ .keep_walking_ = true }; pub const keep_walking = WalkerMut{ .keep_walking = true };
pub const stop = WalkerMut{ .keep_walking_ = false }; pub const stop = WalkerMut{ .keep_walking = false };
pub const found = WalkerMut{ .found_ = true }; pub const found = WalkerMut{ .found = true };
const F = *const fn (ctx: *anyopaque, leaf: *const Leaf, metrics: Metrics) WalkerMut; const F = *const fn (ctx: *anyopaque, leaf: *const Leaf, metrics: Metrics) WalkerMut;
}; };
pub const Walker = struct { pub const Walker = struct {
keep_walking_: bool = false, keep_walking: bool = false,
found_: bool = false, found: bool = false,
err: ?anyerror = null, err: ?anyerror = null,
pub const keep_walking = Walker{ .keep_walking_ = true }; pub const keep_walking = Walker{ .keep_walking = true };
pub const stop = Walker{ .keep_walking_ = false }; pub const stop = Walker{ .keep_walking = false };
pub const found = Walker{ .found_ = true }; pub const found = Walker{ .found = true };
const F = *const fn (ctx: *anyopaque, leaf: *const Leaf, metrics: Metrics) Walker; const F = *const fn (ctx: *anyopaque, leaf: *const Leaf, metrics: Metrics) Walker;
}; };
@ -126,8 +126,8 @@ pub const Branch = struct {
fn merge_results_const(_: *const Branch, left: Walker, right: Walker) Walker { fn merge_results_const(_: *const Branch, left: Walker, right: Walker) Walker {
var result = Walker{}; var result = Walker{};
result.err = if (left.err) |_| left.err else right.err; result.err = if (left.err) |_| left.err else right.err;
result.keep_walking_ = left.keep_walking_ and right.keep_walking_; result.keep_walking = left.keep_walking and right.keep_walking;
result.found_ = left.found_ or right.found_; result.found = left.found or right.found;
return result; return result;
} }
@ -144,8 +144,8 @@ pub const Branch = struct {
else else
Node.new(allocator, new_left, new_right) catch |e| return .{ .err = e }; Node.new(allocator, new_left, new_right) catch |e| return .{ .err = e };
} }
result.keep_walking_ = left.keep_walking_ and right.keep_walking_; result.keep_walking = left.keep_walking and right.keep_walking;
result.found_ = left.found_ or right.found_; result.found = left.found or right.found;
return result; return result;
} }
}; };
@ -350,10 +350,10 @@ const Node = union(enum) {
switch (self.*) { switch (self.*) {
.node => |*node| { .node => |*node| {
const left = node.left.walk_const(f, ctx, metrics); const left = node.left.walk_const(f, ctx, metrics);
if (!left.keep_walking_) { if (!left.keep_walking) {
var result = Walker{}; var result = Walker{};
result.err = left.err; result.err = left.err;
result.found_ = left.found_; result.found = left.found;
return result; return result;
} }
const right = node.right.walk_const(f, ctx, metrics); const right = node.right.walk_const(f, ctx, metrics);
@ -367,10 +367,10 @@ const Node = union(enum) {
switch (self.*) { switch (self.*) {
.node => |*node| { .node => |*node| {
const left = node.left.walk(allocator, f, ctx, metrics); const left = node.left.walk(allocator, f, ctx, metrics);
if (!left.keep_walking_) { if (!left.keep_walking) {
var result = WalkerMut{}; var result = WalkerMut{};
result.err = left.err; result.err = left.err;
result.found_ = left.found_; result.found = left.found;
if (left.replace) |p| { if (left.replace) |p| {
result.replace = Node.new(allocator, p, node.right) catch |e| return .{ .err = e }; result.replace = Node.new(allocator, p, node.right) catch |e| return .{ .err = e };
} }
@ -390,14 +390,14 @@ const Node = union(enum) {
if (line >= left_bols) if (line >= left_bols)
return node.right.walk_from_line_begin_const_internal(line - left_bols, f, ctx, metrics); return node.right.walk_from_line_begin_const_internal(line - left_bols, f, ctx, metrics);
const left_result = node.left.walk_from_line_begin_const_internal(line, f, ctx, metrics); const left_result = node.left.walk_from_line_begin_const_internal(line, f, ctx, metrics);
const right_result = if (left_result.found_ and left_result.keep_walking_) node.right.walk_const(f, ctx, metrics) else Walker{}; const right_result = if (left_result.found and left_result.keep_walking) node.right.walk_const(f, ctx, metrics) else Walker{};
return node.merge_results_const(left_result, right_result); return node.merge_results_const(left_result, right_result);
}, },
.leaf => |*l| { .leaf => |*l| {
if (line == 0) { if (line == 0) {
var result = f(ctx, l, metrics); var result = f(ctx, l, metrics);
if (result.err) |_| return result; if (result.err) |_| return result;
result.found_ = true; result.found = true;
return result; return result;
} }
return Walker.keep_walking; return Walker.keep_walking;
@ -408,7 +408,7 @@ const Node = union(enum) {
pub fn walk_from_line_begin_const(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque, metrics: Metrics) !bool { pub fn walk_from_line_begin_const(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque, metrics: Metrics) !bool {
const result = self.walk_from_line_begin_const_internal(line, f, ctx, metrics); const result = self.walk_from_line_begin_const_internal(line, f, ctx, metrics);
if (result.err) |e| return e; if (result.err) |e| return e;
return result.found_; return result.found;
} }
fn walk_from_line_begin_internal(self: *const Node, allocator: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque, metrics: Metrics) WalkerMut { fn walk_from_line_begin_internal(self: *const Node, allocator: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque, metrics: Metrics) WalkerMut {
@ -420,8 +420,8 @@ const Node = union(enum) {
if (right_result.replace) |p| { if (right_result.replace) |p| {
var result = WalkerMut{}; var result = WalkerMut{};
result.err = right_result.err; result.err = right_result.err;
result.found_ = right_result.found_; result.found = right_result.found;
result.keep_walking_ = right_result.keep_walking_; result.keep_walking = right_result.keep_walking;
result.replace = if (p.is_empty()) result.replace = if (p.is_empty())
node.left node.left
else else
@ -432,7 +432,7 @@ const Node = union(enum) {
} }
} }
const left_result = node.left.walk_from_line_begin_internal(allocator, line, f, ctx, metrics); const left_result = node.left.walk_from_line_begin_internal(allocator, line, f, ctx, metrics);
const right_result = if (left_result.found_ and left_result.keep_walking_) node.right.walk(allocator, f, ctx, metrics) else WalkerMut{}; const right_result = if (left_result.found and left_result.keep_walking) node.right.walk(allocator, f, ctx, metrics) else WalkerMut{};
return node.merge_results(allocator, left_result, right_result); return node.merge_results(allocator, left_result, right_result);
}, },
.leaf => |*l| { .leaf => |*l| {
@ -442,7 +442,7 @@ const Node = union(enum) {
result.replace = null; result.replace = null;
return result; return result;
} }
result.found_ = true; result.found = true;
return result; return result;
} }
return WalkerMut.keep_walking; return WalkerMut.keep_walking;
@ -453,7 +453,7 @@ const Node = union(enum) {
pub fn walk_from_line_begin(self: *const Node, allocator: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque, metrics: Metrics) !struct { bool, ?Root } { pub fn walk_from_line_begin(self: *const Node, allocator: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque, metrics: Metrics) !struct { bool, ?Root } {
const result = self.walk_from_line_begin_internal(allocator, line, f, ctx, metrics); const result = self.walk_from_line_begin_internal(allocator, line, f, ctx, metrics);
if (result.err) |e| return e; if (result.err) |e| return e;
return .{ result.found_, result.replace }; return .{ result.found, result.replace };
} }
fn find_line_node(self: *const Node, line: usize) ?*const Node { fn find_line_node(self: *const Node, line: usize) ?*const Node {
@ -508,12 +508,12 @@ const Node = union(enum) {
if (ret.err) |e| return .{ .err = e }; if (ret.err) |e| return .{ .err = e };
buf = buf[bytes..]; buf = buf[bytes..];
ctx.abs_col += @intCast(cols); ctx.abs_col += @intCast(cols);
if (!ret.keep_walking_) return Walker.stop; if (!ret.keep_walking) return Walker.stop;
} }
if (leaf.eol) { if (leaf.eol) {
const ret = ctx.walker_f(ctx.walker_ctx, "\n", 1, metrics); const ret = ctx.walker_f(ctx.walker_ctx, "\n", 1, metrics);
if (ret.err) |e| return .{ .err = e }; if (ret.err) |e| return .{ .err = e };
if (!ret.keep_walking_) return Walker.stop; if (!ret.keep_walking) return Walker.stop;
ctx.abs_col = 0; ctx.abs_col = 0;
} }
return Walker.keep_walking; return Walker.keep_walking;
@ -670,7 +670,7 @@ const Node = union(enum) {
var result = WalkerMut.keep_walking; var result = WalkerMut.keep_walking;
if (ctx.delete_next_bol and ctx.bytes == 0) { if (ctx.delete_next_bol and ctx.bytes == 0) {
result.replace = Leaf.new(ctx.allocator, leaf.buf, false, leaf.eol) catch |e| return .{ .err = e }; result.replace = Leaf.new(ctx.allocator, leaf.buf, false, leaf.eol) catch |e| return .{ .err = e };
result.keep_walking_ = false; result.keep_walking = false;
ctx.delete_next_bol = false; ctx.delete_next_bol = false;
return result; return result;
} }
@ -724,7 +724,7 @@ const Node = union(enum) {
} }
} }
if (ctx.bytes == 0 and !ctx.delete_next_bol) if (ctx.bytes == 0 and !ctx.delete_next_bol)
result.keep_walking_ = false; result.keep_walking = false;
} }
return result; return result;
} }
@ -1211,9 +1211,6 @@ pub const LoadFromFileError = error{
DanglingSurrogateHalf, DanglingSurrogateHalf,
ExpectedSecondSurrogateHalf, ExpectedSecondSurrogateHalf,
UnexpectedSecondSurrogateHalf, UnexpectedSecondSurrogateHalf,
LockViolation,
ProcessNotFound,
Canceled,
}; };
pub fn load_from_file( pub fn load_from_file(
@ -1257,11 +1254,6 @@ pub fn reset_to_last_saved(self: *Self) void {
} }
} }
pub fn refresh_from_file(self: *Self) LoadFromFileError!void {
try self.load_from_file_and_update(self.file_path);
self.update_last_used_time();
}
pub fn store_to_string(self: *const Self, allocator: Allocator, eol_mode: EolMode) ![]u8 { pub fn store_to_string(self: *const Self, allocator: Allocator, eol_mode: EolMode) ![]u8 {
var s = try ArrayList(u8).initCapacity(allocator, self.root.weights_sum().len); var s = try ArrayList(u8).initCapacity(allocator, self.root.weights_sum().len);
try self.root.store(s.writer(), eol_mode); try self.root.store(s.writer(), eol_mode);
@ -1310,7 +1302,6 @@ pub const StoreToFileError = error{
PathAlreadyExists, PathAlreadyExists,
PipeBusy, PipeBusy,
ProcessFdQuotaExceeded, ProcessFdQuotaExceeded,
ProcessNotFound,
ReadOnlyFileSystem, ReadOnlyFileSystem,
RenameAcrossMountPoints, RenameAcrossMountPoints,
SharingViolation, SharingViolation,

View file

@ -13,14 +13,10 @@ pub const Context = struct {
args: tp.message = .{}, args: tp.message = .{},
pub fn fmt(value: anytype) Context { pub fn fmt(value: anytype) Context {
context_buffer.clearRetainingCapacity(); return .{ .args = tp.message.fmtbuf(&context_buffer, value) catch @panic("command.Context.fmt failed") };
cbor.writeValue(context_buffer.writer(), value) catch @panic("command.Context.fmt failed");
return .{ .args = .{ .buf = context_buffer.items } };
} }
}; };
threadlocal var context_buffer: [tp.max_message_size]u8 = undefined;
const context_buffer_allocator = std.heap.c_allocator;
threadlocal var context_buffer: std.ArrayList(u8) = std.ArrayList(u8).init(context_buffer_allocator);
pub const fmt = Context.fmt; pub const fmt = Context.fmt;
const Vtable = struct { const Vtable = struct {
@ -150,8 +146,13 @@ pub fn execute(id: ID, ctx: Context) tp.result {
} }
pub fn get_id(name: []const u8) ?ID { pub fn get_id(name: []const u8) ?ID {
var id: ?ID = null; for (commands.items) |cmd| {
return get_id_cache(name, &id); if (cmd) |p|
if (std.mem.eql(u8, p.name, name))
return p.id;
}
tp.trace(tp.channel.debug, .{ "command", "get_id", "failed", name });
return null;
} }
pub fn get_name(id: ID) ?[]const u8 { pub fn get_name(id: ID) ?[]const u8 {
@ -173,7 +174,6 @@ pub fn get_id_cache(name: []const u8, id: *?ID) ?ID {
return p.id; return p.id;
}; };
} }
tp.trace(tp.channel.debug, .{ "command", "get_id_cache", "failed", name });
return null; return null;
} }
@ -216,7 +216,7 @@ fn getTargetType(comptime Namespace: type) type {
fn getCommands(comptime Namespace: type) []const CmdDef(*getTargetType(Namespace)) { fn getCommands(comptime Namespace: type) []const CmdDef(*getTargetType(Namespace)) {
@setEvalBranchQuota(10_000); @setEvalBranchQuota(10_000);
comptime switch (@typeInfo(Namespace)) { comptime switch (@typeInfo(Namespace)) {
.@"struct" => |info| { .Struct => |info| {
var count = 0; var count = 0;
const Target = getTargetType(Namespace); const Target = getTargetType(Namespace);
// @compileLog(Namespace, Target); // @compileLog(Namespace, Target);
@ -257,14 +257,14 @@ pub fn Collection(comptime Namespace: type) type {
fields_var[i] = .{ fields_var[i] = .{
.name = cmd.name, .name = cmd.name,
.type = Clsr, .type = Clsr,
.default_value_ptr = null, .default_value = null,
.is_comptime = false, .is_comptime = false,
.alignment = if (@sizeOf(Clsr) > 0) @alignOf(Clsr) else 0, .alignment = if (@sizeOf(Clsr) > 0) @alignOf(Clsr) else 0,
}; };
} }
const fields: [cmds.len]std.builtin.Type.StructField = fields_var; const fields: [cmds.len]std.builtin.Type.StructField = fields_var;
const Fields = @Type(.{ const Fields = @Type(.{
.@"struct" = .{ .Struct = .{
.is_tuple = false, .is_tuple = false,
.layout = .auto, .layout = .auto,
.decls = &.{}, .decls = &.{},

View file

@ -1,65 +0,0 @@
const std = @import("std");
const tp = @import("thespian");
const OutOfMemoryError = error{OutOfMemory};
const SpawnError = error{ThespianSpawnFailed};
pub fn send(
allocator: std.mem.Allocator,
to: tp.pid_ref,
m: anytype,
ctx: anytype,
) (OutOfMemoryError || SpawnError)!void {
return RequestContext(@TypeOf(ctx)).send(allocator, to, ctx, tp.message.fmt(m));
}
fn RequestContext(T: type) type {
return struct {
receiver: ReceiverT,
ctx: T,
to: tp.pid,
request: tp.message,
response: ?tp.message,
a: std.mem.Allocator,
const Self = @This();
const ReceiverT = tp.Receiver(*@This());
fn send(a: std.mem.Allocator, to: tp.pid_ref, ctx: T, request: tp.message) (OutOfMemoryError || SpawnError)!void {
const self = try a.create(@This());
self.* = .{
.receiver = undefined,
.ctx = if (@hasDecl(T, "clone")) ctx.clone() else ctx,
.to = to.clone(),
.request = try request.clone(std.heap.c_allocator),
.response = null,
.a = a,
};
self.receiver = ReceiverT.init(receive_, self);
const proc = try tp.spawn_link(a, self, start, @typeName(@This()));
defer proc.deinit();
}
fn deinit(self: *@This()) void {
if (@hasDecl(T, "deinit")) self.ctx.deinit();
std.heap.c_allocator.free(self.request.buf);
self.to.deinit();
self.a.destroy(self);
}
fn start(self: *@This()) tp.result {
_ = tp.set_trap(true);
if (@hasDecl(T, "link")) try self.ctx.link();
errdefer self.deinit();
try self.to.link();
try self.to.send_raw(self.request);
tp.receive(&self.receiver);
}
fn receive_(self: *@This(), _: tp.pid_ref, m: tp.message) tp.result {
defer self.deinit();
self.ctx.receive(m) catch |e| return tp.exit_error(e, @errorReturnTrace());
return tp.exit_normal();
}
};
}

View file

@ -10,8 +10,6 @@ enable_terminal_cursor: bool = true,
enable_terminal_color_scheme: bool = builtin.os.tag != .windows, enable_terminal_color_scheme: bool = builtin.os.tag != .windows,
highlight_current_line: bool = true, highlight_current_line: bool = true,
highlight_current_line_gutter: bool = true, highlight_current_line_gutter: bool = true,
highlight_columns: []const u8 = "80 100 120",
highlight_columns_alpha: u8 = 240,
whitespace_mode: []const u8 = "none", whitespace_mode: []const u8 = "none",
inline_diagnostics: bool = true, inline_diagnostics: bool = true,
animation_min_lag: usize = 0, //milliseconds animation_min_lag: usize = 0, //milliseconds
@ -27,7 +25,7 @@ bottom_bar: []const u8 = "mode file log selection diagnostics keybind linenumber
show_scrollbars: bool = true, show_scrollbars: bool = true,
show_fileicons: bool = true, show_fileicons: bool = true,
start_debugger_on_crash: bool = false, lsp_request_timeout: usize = 10,
include_files: []const u8 = "", include_files: []const u8 = "",

View file

@ -1,122 +0,0 @@
const std = @import("std");
const tp = @import("thespian");
const shell = @import("shell");
const bin_path = @import("bin_path");
pub const Error = error{ OutOfMemory, GitNotFound, GitCallFailed };
const log_execute = false;
pub fn workspace_path(context_: usize) Error!void {
const fn_name = @src().fn_name;
try git(context_, .{ "rev-parse", "--show-toplevel" }, struct {
fn result(context: usize, parent: tp.pid_ref, output: []const u8) void {
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |value| if (value.len > 0)
parent.send(.{ module_name, context, fn_name, value }) catch {};
}
}.result, exit_null_on_error(fn_name));
}
pub fn current_branch(context_: usize) Error!void {
const fn_name = @src().fn_name;
try git(context_, .{ "rev-parse", "--abbrev-ref", "HEAD" }, struct {
fn result(context: usize, parent: tp.pid_ref, output: []const u8) void {
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |value| if (value.len > 0)
parent.send(.{ module_name, context, fn_name, value }) catch {};
}
}.result, exit_null_on_error(fn_name));
}
pub fn workspace_files(context_: usize) Error!void {
const fn_name = @src().fn_name;
try git_err(context_, .{ "ls-files", "--cached", "--others", "--exclude-standard" }, struct {
fn result(context: usize, parent: tp.pid_ref, output: []const u8) void {
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |value| if (value.len > 0)
parent.send(.{ module_name, context, fn_name, value }) catch {};
}
}.result, struct {
fn result(_: usize, _: tp.pid_ref, output: []const u8) void {
var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |line| std.log.err("{s}: {s}", .{ module_name, line });
}
}.result, exit_null_on_error(fn_name));
}
fn git(
context: usize,
cmd: anytype,
out: OutputHandler,
exit: ExitHandler,
) Error!void {
return git_err(context, cmd, out, noop, exit);
}
fn git_err(
context: usize,
cmd: anytype,
out: OutputHandler,
err: OutputHandler,
exit: ExitHandler,
) Error!void {
const cbor = @import("cbor");
const git_binary = get_git() orelse return error.GitNotFound;
var buf: std.ArrayListUnmanaged(u8) = .empty;
const writer = buf.writer(allocator);
switch (@typeInfo(@TypeOf(cmd))) {
.@"struct" => |info| if (info.is_tuple) {
try cbor.writeArrayHeader(writer, info.fields.len + 1);
try cbor.writeValue(writer, git_binary);
inline for (info.fields) |f|
try cbor.writeValue(writer, @field(cmd, f.name));
return shell.execute(allocator, .{ .buf = buf.items }, .{
.context = context,
.out = to_shell_output_handler(out),
.err = to_shell_output_handler(err),
.exit = exit,
.log_execute = log_execute,
}) catch error.GitCallFailed;
},
else => {},
}
@compileError("git command should be a tuple: " ++ @typeName(@TypeOf(cmd)));
}
fn exit_null_on_error(comptime tag: []const u8) shell.ExitHandler {
return struct {
fn exit(_: usize, parent: tp.pid_ref, _: []const u8, _: []const u8, exit_code: i64) void {
if (exit_code > 0)
parent.send(.{ module_name, tag, null }) catch {};
}
}.exit;
}
const OutputHandler = fn (context: usize, parent: tp.pid_ref, output: []const u8) void;
const ExitHandler = shell.ExitHandler;
fn to_shell_output_handler(handler: anytype) shell.OutputHandler {
return struct {
fn out(context: usize, parent: tp.pid_ref, _: []const u8, output: []const u8) void {
handler(context, parent, output);
}
}.out;
}
fn noop(_: usize, _: tp.pid_ref, _: []const u8) void {}
var git_path: ?struct {
path: ?[:0]const u8 = null,
} = null;
const allocator = std.heap.c_allocator;
fn get_git() ?[]const u8 {
if (git_path) |p| return p.path;
const path = bin_path.find_binary_in_path(allocator, module_name) catch null;
git_path = .{ .path = path };
return path;
}
const module_name = @typeName(@This());

View file

@ -47,17 +47,6 @@
["ctrl+x ctrl+r", "open_recent"], ["ctrl+x ctrl+r", "open_recent"],
["ctrl+space", "enter_mode", "select"], ["ctrl+space", "enter_mode", "select"],
["alt+0", "add_integer_argument_digit", 0],
["alt+1", "add_integer_argument_digit", 1],
["alt+2", "add_integer_argument_digit", 2],
["alt+3", "add_integer_argument_digit", 3],
["alt+4", "add_integer_argument_digit", 4],
["alt+5", "add_integer_argument_digit", 5],
["alt+6", "add_integer_argument_digit", 6],
["alt+7", "add_integer_argument_digit", 7],
["alt+8", "add_integer_argument_digit", 8],
["alt+9", "add_integer_argument_digit", 9],
["ctrl+c l = =", "format"], ["ctrl+c l = =", "format"],
["ctrl+c l = r", "format"], ["ctrl+c l = r", "format"],
["ctrl+c l g g", "goto_definition"], ["ctrl+c l g g", "goto_definition"],
@ -66,7 +55,6 @@
["ctrl+c l g r", "references"], ["ctrl+c l g r", "references"],
["ctrl+c l h h", "hover"], ["ctrl+c l h h", "hover"],
["ctrl+c l r r", "rename_symbol"], ["ctrl+c l r r", "rename_symbol"],
["ctrl+c f", "copy_file_name"],
["super+l = =", "format"], ["super+l = =", "format"],
["super+l = r", "format"], ["super+l = r", "format"],

View file

@ -50,12 +50,10 @@
["ctrl+l", "scroll_view_center_cycle"], ["ctrl+l", "scroll_view_center_cycle"],
["ctrl+n", "goto_next_match"], ["ctrl+n", "goto_next_match"],
["ctrl+p", "goto_prev_match"], ["ctrl+p", "goto_prev_match"],
["ctrl+b", "move_to_char", "move_or_select_to_char_left"], ["ctrl+b", "move_to_char", "left"],
["ctrl+t", "move_to_char", "move_or_select_to_char_right"], ["ctrl+t", "move_to_char", "right"],
["ctrl+x", "cut"], ["ctrl+x", "cut"],
["ctrl+c", "copy"], ["ctrl+c", "copy"],
["alt+C", "copy_file_name"],
["ctrl+k alt+c", "copy_file_name", "file_name_only"],
["ctrl+v", "system_paste"], ["ctrl+v", "system_paste"],
["ctrl+u", "pop_cursor"], ["ctrl+u", "pop_cursor"],
["ctrl+k m", "change_file_type"], ["ctrl+k m", "change_file_type"],
@ -192,17 +190,6 @@
["«", "smart_insert_pair", "«", "»"], ["«", "smart_insert_pair", "«", "»"],
["»", "smart_insert_pair_close", "«", "»"], ["»", "smart_insert_pair_close", "«", "»"],
["alt+0", "add_integer_argument_digit", 0],
["alt+1", "add_integer_argument_digit", 1],
["alt+2", "add_integer_argument_digit", 2],
["alt+3", "add_integer_argument_digit", 3],
["alt+4", "add_integer_argument_digit", 4],
["alt+5", "add_integer_argument_digit", 5],
["alt+6", "add_integer_argument_digit", 6],
["alt+7", "add_integer_argument_digit", 7],
["alt+8", "add_integer_argument_digit", 8],
["alt+9", "add_integer_argument_digit", 9],
["left_control", "enable_jump_mode"], ["left_control", "enable_jump_mode"],
["right_control", "enable_jump_mode"], ["right_control", "enable_jump_mode"],
["left_alt", "enable_fast_scroll"], ["left_alt", "enable_fast_scroll"],
@ -400,7 +387,7 @@
["f15", "goto_prev_match"], ["f15", "goto_prev_match"],
["f9", "theme_prev"], ["f9", "theme_prev"],
["f10", "theme_next"], ["f10", "theme_next"],
["escape", "mini_mode_cancel"], ["escape", "exit_mini_mode"],
["enter", "mini_mode_select"], ["enter", "mini_mode_select"],
["backspace", "mini_mode_delete_backwards"] ["backspace", "mini_mode_delete_backwards"]
] ]

View file

@ -62,14 +62,14 @@
["shift+`", "switch_case"], ["shift+`", "switch_case"],
["shift+t", "till_prev_char"], ["shift+t", "till_prev_char"],
["shift+f", "move_to_char", "move_to_char_left"], ["shift+f", "move_to_char", false],
["shift+w", "move_next_long_word_start"], ["shift+w", "move_next_long_word_start"],
["shift+b", "move_prev_long_word_start"], ["shift+b", "move_prev_long_word_start"],
["shift+e", "move_next_long_word_end"], ["shift+e", "move_next_long_word_end"],
["shift+i", ["enter_mode", "insert"], ["smart_move_begin"]], ["shift+i", ["smart_move_begin"], ["enter_mode", "insert"]],
["shift+a", ["enter_mode", "insert"], ["move_end"]], ["shift+a", ["move_end"], ["enter_mode", "insert"]],
["shift+o", ["enter_mode", "insert"], ["smart_insert_line_before"]], ["shift+o", ["smart_insert_line_before"], ["enter_mode", "insert"]],
["shift+c", "copy_selection_on_next_line"], ["shift+c", "copy_selection_on_next_line"],
["shift+s", "split_selection"], ["shift+s", "split_selection"],
@ -107,7 +107,7 @@
["l", "move_right"], ["l", "move_right"],
["t", "find_till_char"], ["t", "find_till_char"],
["f", "move_to_char", "move_to_char_right"], ["f", "move_to_char", true],
["`", "to_lower"], ["`", "to_lower"],
@ -120,7 +120,7 @@
["v", "enter_mode", "select"], ["v", "enter_mode", "select"],
["g g", "goto_line_vim"], ["g g", "move_buffer_begin"],
["g e", "move_buffer_end"], ["g e", "move_buffer_end"],
["g f", "goto_file"], ["g f", "goto_file"],
["g h", "move_begin"], ["g h", "move_begin"],
@ -143,11 +143,11 @@
["g shift+d", "goto_declaration"], ["g shift+d", "goto_declaration"],
["i", "enter_mode", "insert"], ["i", "enter_mode", "insert"],
["a", ["enter_mode", "insert"], ["move_right"]], ["a", ["move_right"], ["enter_mode", "insert"]],
["o", ["enter_mode", "insert"], ["smart_insert_line_after"]], ["o", ["smart_insert_line_after"], ["enter_mode", "insert"]],
["d", "cut_forward_internal_inclusive"], ["d", "cut"],
["c", ["enable_selection"], ["enter_mode", "insert"], ["cut_forward_internal_inclusive"]], ["c", ["cut"], ["enter_mode", "insert"]],
["s", "select_regex"], ["s", "select_regex"],
[";", "collapse_selections"], [";", "collapse_selections"],
@ -190,7 +190,7 @@
["n", "goto_next_match"], ["n", "goto_next_match"],
["u", "undo"], ["u", "undo"],
["y", ["enable_selection"], ["copy_helix"], ["enter_mode", "normal"]], ["y", "copy"],
["p", "paste_after"], ["p", "paste_after"],
["q", "record_macro"], ["q", "record_macro"],
@ -204,13 +204,13 @@
["page_up", "move_scroll_page_up"], ["page_up", "move_scroll_page_up"],
["page_down", "move_scroll_page_down"], ["page_down", "move_scroll_page_down"],
["space shift+f", "find_file"], ["space shift+f", "file_picker_in_current_directory"],
["space shift+s", "workspace_symbol_picker"], ["space shift+s", "workspace_symbol_picker"],
["space shift+d", "workspace_diagnostics_picker"], ["space shift+d", "workspace_diagnostics_picker"],
["space shift+p", "system_paste"], ["space shift+p", "system_paste"],
["space shift+r", "replace_selections_with_clipboard"], ["space shift+r", "replace_selections_with_clipboard"],
["space shift+/", "open_command_palette"], ["space shift+/", "open_command_palette"],
["space f", "find_file"], ["space f", "file_picker"],
["space b", "buffer_picker"], ["space b", "buffer_picker"],
["space j", "jumplist_picker"], ["space j", "jumplist_picker"],
["space s", "symbol_picker"], ["space s", "symbol_picker"],
@ -225,24 +225,21 @@
["space h", "select_references_to_symbol_under_cursor"], ["space h", "select_references_to_symbol_under_cursor"],
["space c", "toggle_comment"], ["space c", "toggle_comment"],
["0", "add_integer_argument_digit", 0], ["1", "add_count", 1],
["1", "add_integer_argument_digit", 1], ["2", "add_count", 2],
["2", "add_integer_argument_digit", 2], ["3", "add_count", 3],
["3", "add_integer_argument_digit", 3], ["4", "add_count", 4],
["4", "add_integer_argument_digit", 4], ["5", "add_count", 5],
["5", "add_integer_argument_digit", 5], ["6", "add_count", 6],
["6", "add_integer_argument_digit", 6], ["7", "add_count", 7],
["7", "add_integer_argument_digit", 7], ["8", "add_count", 8],
["8", "add_integer_argument_digit", 8], ["9", "add_count", 9]
["9", "add_integer_argument_digit", 9]
] ]
}, },
"insert": { "insert": {
"name": "INS", "name": "INS",
"line_numbers": "absolute", "line_numbers": "absolute",
"cursor": "beam", "cursor": "beam",
"init_command": ["pause_undo_history"],
"deinit_command": ["resume_undo_history"],
"press": [ "press": [
["ctrl+u", "move_scroll_page_up"], ["ctrl+u", "move_scroll_page_up"],
["ctrl+d", "move_scroll_page_down"], ["ctrl+d", "move_scroll_page_down"],
@ -257,7 +254,6 @@
"line_numbers": "relative", "line_numbers": "relative",
"cursor": "block", "cursor": "block",
"selection": "inclusive", "selection": "inclusive",
"init_command": ["enable_selection"],
"press": [ "press": [
["ctrl+b", "select_page_up"], ["ctrl+b", "select_page_up"],
["ctrl+f", "select_page_down"], ["ctrl+f", "select_page_down"],
@ -280,7 +276,7 @@
["alt+`", "switch_to_uppercase"], ["alt+`", "switch_to_uppercase"],
["alt+d", "delete_backward"], ["alt+d", "delete_backward"],
["alt+c", ["enter_mode", "insert"], ["delete_backward"]], ["alt+c", ["delete_backward"], ["enter_mode", "insert"]],
["alt+s", "split_selection_on_newline"], ["alt+s", "split_selection_on_newline"],
["alt+-", "merge_selections"], ["alt+-", "merge_selections"],
@ -324,7 +320,7 @@
["shift+`", "switch_case"], ["shift+`", "switch_case"],
["shift+t", "extend_till_prev_char"], ["shift+t", "extend_till_prev_char"],
["shift+f", "move_to_char", "select_to_char_left_vim"], ["shift+f", "extend_prev_char"],
["shift+w", "extend_next_long_word_start"], ["shift+w", "extend_next_long_word_start"],
["shift+b", "extend_prev_long_word_start"], ["shift+b", "extend_prev_long_word_start"],
@ -332,10 +328,10 @@
["shift+g", "move_buffer_end_or_count_line"], ["shift+g", "move_buffer_end_or_count_line"],
["shift+i", ["enter_mode", "insert"], ["smart_move_begin"]], ["shift+i", ["smart_move_begin"], ["enter_mode", "insert"]],
["shift+a", ["enter_mode", "insert"], ["move_end"]], ["shift+a", ["move_end"], ["enter_mode", "insert"]],
["shift+o", ["enter_mode", "insert"], ["smart_insert_line_before"]], ["shift+o", ["smart_insert_line_before"], ["enter_mode", "insert"]],
["shift+c", "copy_selection_on_next_line"], ["shift+c", "copy_selection_on_next_line"],
@ -373,17 +369,17 @@
["shift+1", "shell_insert_output"], ["shift+1", "shell_insert_output"],
["shift+4", "shell_keep_pipe"], ["shift+4", "shell_keep_pipe"],
["h", "select_left_helix"], ["h", "select_left"],
["j", "select_down"], ["j", "select_down"],
["k", "select_up"], ["k", "select_up"],
["l", "select_right_helix"], ["l", "select_right"],
["left", "select_left"], ["left", "select_left"],
["down", "select_down"], ["down", "select_down"],
["up", "select_up"], ["up", "select_up"],
["right", "select_right"], ["right", "select_right"],
["t", "extend_till_char"], ["t", "extend_till_char"],
["f", "move_to_char", "select_to_char_right_helix"], ["f", "extend_next_char"],
["`", "switch_to_lowercase"], ["`", "switch_to_lowercase"],
@ -400,7 +396,7 @@
["g e", "move_buffer_end"], ["g e", "move_buffer_end"],
["g f", "goto_file"], ["g f", "goto_file"],
["g h", "move_begin"], ["g h", "move_begin"],
["g l", "select_end"], ["g l", "move_end"],
["g s", "smart_move_begin"], ["g s", "smart_move_begin"],
["g d", "goto_definition"], ["g d", "goto_definition"],
["g y", "goto_type_definition"], ["g y", "goto_type_definition"],
@ -419,11 +415,11 @@
["g shift+d", "goto_declaration"], ["g shift+d", "goto_declaration"],
["i", "enter_mode", "insert"], ["i", "enter_mode", "insert"],
["a", ["enter_mode", "insert"], ["move_right"]], ["a", ["move_right"], ["enter_mode", "insert"]],
["o", ["enter_mode", "insert"], ["smart_insert_line_after"]], ["o", ["smart_insert_line_after"], ["enter_mode", "insert"]],
["d", ["cut"], ["enter_mode", "normal"]], ["d", "cut"],
["c", ["enter_mode", "insert"], ["cut"]], ["c", ["cut"], ["enter_mode", "insert"]],
["s", "select_regex"], ["s", "select_regex"],
[";", "collapse_selections"], [";", "collapse_selections"],
@ -468,7 +464,7 @@
["n", "goto_next_match"], ["n", "goto_next_match"],
["u", "undo"], ["u", "undo"],
["y", ["copy_helix"], ["enter_mode", "normal"]], ["y", "copy"],
["p", "paste_after"], ["p", "paste_after"],
["q", "record_macro"], ["q", "record_macro"],
@ -501,16 +497,15 @@
["space h", "select_references_to_symbol_under_cursor"], ["space h", "select_references_to_symbol_under_cursor"],
["space c", "toggle_comment"], ["space c", "toggle_comment"],
["0", "add_integer_argument_digit", 0], ["1", "add_count", 1],
["1", "add_integer_argument_digit", 1], ["2", "add_count", 2],
["2", "add_integer_argument_digit", 2], ["3", "add_count", 3],
["3", "add_integer_argument_digit", 3], ["4", "add_count", 4],
["4", "add_integer_argument_digit", 4], ["5", "add_count", 5],
["5", "add_integer_argument_digit", 5], ["6", "add_count", 6],
["6", "add_integer_argument_digit", 6], ["7", "add_count", 7],
["7", "add_integer_argument_digit", 7], ["8", "add_count", 8],
["8", "add_integer_argument_digit", 8], ["9", "add_count", 9]
["9", "add_integer_argument_digit", 9]
] ]
}, },
"home": { "home": {

View file

@ -17,7 +17,7 @@
["B", "move_word_left"], ["B", "move_word_left"],
["e", "move_word_right_end_vim"], ["e", "move_word_right_end_vim"],
["x", "cut_forward_internal"], ["x", "cut_forward_internal"],
["s", ["enter_mode", "insert"], ["cut_forward_internal"]], ["s", ["cut_forward_internal"], ["enter_mode", "insert"]],
["u", "undo"], ["u", "undo"],
["j", "move_down_vim"], ["j", "move_down_vim"],
@ -27,11 +27,11 @@
["<Space>", "move_right_vim"], ["<Space>", "move_right_vim"],
["i", "enter_mode", "insert"], ["i", "enter_mode", "insert"],
["a", ["enter_mode", "insert"], ["move_right"]], ["a", ["move_right"], ["enter_mode", "insert"]],
["I", ["enter_mode", "insert"], ["smart_move_begin"]], ["I", ["smart_move_begin"], ["enter_mode", "insert"]],
["A", ["enter_mode", "insert"], ["move_end"]], ["A", ["move_end"], ["enter_mode", "insert"]],
["o", ["enter_mode", "insert"], ["smart_insert_line_after"]], ["o", ["smart_insert_line_after"], ["enter_mode", "insert"]],
["O", ["enter_mode", "insert"], ["smart_insert_line_before"]], ["O", ["smart_insert_line_before"], ["enter_mode", "insert"]],
["<S-.><S-.>", "indent"], ["<S-.><S-.>", "indent"],
["<S-,><S-,>", "unindent"], ["<S-,><S-,>", "unindent"],
@ -41,6 +41,7 @@
["n", "goto_next_match"], ["n", "goto_next_match"],
["N", "goto_prev_match"], ["N", "goto_prev_match"],
["0", "move_begin"],
["^", "smart_move_begin"], ["^", "smart_move_begin"],
["$", "move_end"], ["$", "move_end"],
[":", "open_command_palette"], [":", "open_command_palette"],
@ -51,7 +52,7 @@
["gd", "goto_definition"], ["gd", "goto_definition"],
["gi", "goto_implementation"], ["gi", "goto_implementation"],
["gy", "goto_type_definition"], ["gy", "goto_type_definition"],
["gg", "goto_line_vim"], ["gg", "move_buffer_begin"],
["grn", "rename_symbol"], ["grn", "rename_symbol"],
["gD", "goto_declaration"], ["gD", "goto_declaration"],
["G", "move_buffer_end"], ["G", "move_buffer_end"],
@ -62,11 +63,11 @@
["dd", "cut_internal_vim"], ["dd", "cut_internal_vim"],
["\"_dd", "delete_line"], ["\"_dd", "delete_line"],
["cc", ["enter_mode", "insert"], ["cut_internal_vim"]], ["cc", ["cut_internal_vim"], ["enter_mode", "insert"]],
["C", ["enter_mode", "insert"], ["cut_to_end_vim"]], ["C", ["cut_to_end_vim"], ["enter_mode", "insert"]],
["D", "cut_to_end_vim"], ["D", "cut_to_end_vim"],
["cw", ["enter_mode", "insert"], ["cut_word_right_vim"]], ["cw", ["cut_word_right_vim"], ["enter_mode", "insert"]],
["cb", ["enter_mode", "insert"], ["cut_word_left_vim"]], ["cb", ["cut_word_left_vim"], ["enter_mode", "insert"]],
["yy", ["copy_line_internal_vim"], ["cancel"]], ["yy", ["copy_line_internal_vim"], ["cancel"]],
@ -85,24 +86,11 @@
["<C-k>", "TODO"], ["<C-k>", "TODO"],
["F", "move_to_char", "move_to_char_left"], ["F", "move_to_char", "left"],
["f", "move_to_char", "move_to_char_right"], ["f", "move_to_char", "right"],
["T", "move_to_char", "move_till_char_left"],
["t", "move_to_char", "move_till_char_right"],
["<C-CR>", ["move_down"], ["move_begin"]], ["<C-CR>", ["move_down"], ["move_begin"]],
["<CR>", ["move_down"], ["move_begin"]], ["<CR>", ["move_down"], ["move_begin"]]
["0", "move_begin_or_add_integer_argument_zero"],
["1", "add_integer_argument_digit", 1],
["2", "add_integer_argument_digit", 2],
["3", "add_integer_argument_digit", 3],
["4", "add_integer_argument_digit", 4],
["5", "add_integer_argument_digit", 5],
["6", "add_integer_argument_digit", 6],
["7", "add_integer_argument_digit", 7],
["8", "add_integer_argument_digit", 8],
["9", "add_integer_argument_digit", 9]
] ]
}, },
"visual": { "visual": {
@ -112,7 +100,6 @@
"line_numbers": "relative", "line_numbers": "relative",
"cursor": "block", "cursor": "block",
"selection": "normal", "selection": "normal",
"init_command": ["enable_selection"],
"press": [ "press": [
["<Esc>", ["cancel"], ["enter_mode", "normal"]], ["<Esc>", ["cancel"], ["enter_mode", "normal"]],
["k", "select_up"], ["k", "select_up"],
@ -128,13 +115,7 @@
["0", "move_begin"], ["0", "move_begin"],
["^", "smart_move_begin"], ["^", "smart_move_begin"],
["$", "select_end"], ["$", "move_end"],
[":", "open_command_palette"],
["f", "move_to_char", "select_to_char_right"],
["F", "move_to_char", "select_to_char_left_vim"],
["t", "move_to_char", "select_till_char_right"],
["T", "move_to_char", "select_till_char_left_vim"],
["p", ["paste_internal_vim"], ["enter_mode", "normal"]], ["p", ["paste_internal_vim"], ["enter_mode", "normal"]],
["P", ["paste_internal_vim"], ["enter_mode", "normal"]], ["P", ["paste_internal_vim"], ["enter_mode", "normal"]],
@ -150,10 +131,10 @@
["x", ["cut_forward_internal"], ["enter_mode", "normal"]], ["x", ["cut_forward_internal"], ["enter_mode", "normal"]],
["d", ["cut_forward_internal"], ["enter_mode", "normal"]], ["d", ["cut_forward_internal"], ["enter_mode", "normal"]],
["s", ["enter_mode", "insert"], ["cut_forward_internal"]], ["s", ["cut_forward_internal"], ["enter_mode", "insert"]],
["c", ["enter_mode", "insert"], ["cut_forward_internal"]], ["c", ["cut_forward_internal"], ["enter_mode", "insert"]],
["C", ["enter_mode", "insert"], ["cut_to_end_vim"]], ["C", ["cut_to_end_vim"], ["enter_mode", "insert"]],
["D", "cut_to_end_vim"] ["D", "cut_to_end_vim"]
] ]
}, },
@ -172,7 +153,6 @@
["0", "move_begin"], ["0", "move_begin"],
["^", "smart_move_begin"], ["^", "smart_move_begin"],
["$", "move_end"], ["$", "move_end"],
[":", "open_command_palette"],
["p", ["paste_internal_vim"], ["enter_mode", "normal"]], ["p", ["paste_internal_vim"], ["enter_mode", "normal"]],
["P", ["paste_internal_vim"], ["enter_mode", "normal"]], ["P", ["paste_internal_vim"], ["enter_mode", "normal"]],
@ -187,10 +167,10 @@
["x", ["cut_internal_vim"], ["enter_mode", "normal"]], ["x", ["cut_internal_vim"], ["enter_mode", "normal"]],
["d", ["cut_internal_vim"], ["enter_mode", "normal"]], ["d", ["cut_internal_vim"], ["enter_mode", "normal"]],
["s", ["enter_mode", "insert"], ["cut_internal_vim"]], ["s", ["cut_internal_vim"], ["enter_mode", "insert"]],
["c", ["enter_mode", "insert"], ["cut_internal_vim"]], ["c", ["cut_internal_vim"], ["enter_mode", "insert"]],
["C", ["enter_mode", "insert"], ["cut_to_end_vim"]], ["C", ["cut_to_end_vim"], ["enter_mode", "insert"]],
["D", "cut_to_end_vim"] ["D", "cut_to_end_vim"]
] ]
}, },
@ -199,8 +179,6 @@
"name": "INSERT", "name": "INSERT",
"line_numbers": "absolute", "line_numbers": "absolute",
"cursor": "beam", "cursor": "beam",
"init_command": ["pause_undo_history"],
"deinit_command": ["resume_undo_history"],
"press": [ "press": [
["<Esc>", ["move_left_vim"], ["enter_mode", "normal"]], ["<Esc>", ["move_left_vim"], ["enter_mode", "normal"]],
["<Del>", "delete_forward"], ["<Del>", "delete_forward"],

View file

@ -25,31 +25,6 @@ const builtin_keybinds = std.static_string_map.StaticStringMap([]const u8).initC
.{ "emacs", @embedFile("builtin/emacs.json") }, .{ "emacs", @embedFile("builtin/emacs.json") },
}); });
var integer_argument: ?usize = null;
var commands: Commands = undefined;
const Commands = command.Collection(struct {
pub const Target = void;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn add_integer_argument_digit(_: *void, ctx: Ctx) Result {
var digit: usize = undefined;
if (!try ctx.args.match(.{tp.extract(&digit)}))
return error.InvalidIntegerParameterArgument;
if (digit > 9)
return error.InvalidIntegerParameterDigit;
integer_argument = if (integer_argument) |x| x * 10 + digit else digit;
}
pub const add_integer_argument_digit_meta: Meta = .{ .arguments = &.{.integer} };
});
pub fn init() !void {
var v: void = {};
try commands.init(&v);
}
pub fn mode(mode_name: []const u8, allocator: std.mem.Allocator, opts: anytype) !Mode { pub fn mode(mode_name: []const u8, allocator: std.mem.Allocator, opts: anytype) !Mode {
return Handler.create(mode_name, allocator, opts) catch |e| switch (e) { return Handler.create(mode_name, allocator, opts) catch |e| switch (e) {
error.NotFound => return error.Stop, error.NotFound => return error.Stop,
@ -86,8 +61,6 @@ const Handler = struct {
.line_numbers = self.bindings.line_numbers, .line_numbers = self.bindings.line_numbers,
.cursor_shape = self.bindings.cursor_shape, .cursor_shape = self.bindings.cursor_shape,
.selection_style = self.bindings.selection_style, .selection_style = self.bindings.selection_style,
.init_command = self.bindings.init_command,
.deinit_command = self.bindings.deinit_command,
}; };
} }
pub fn deinit(self: *@This()) void { pub fn deinit(self: *@This()) void {
@ -109,20 +82,8 @@ pub const Mode = struct {
keybind_hints: *const KeybindHints, keybind_hints: *const KeybindHints,
cursor_shape: ?CursorShape = null, cursor_shape: ?CursorShape = null,
selection_style: SelectionStyle, selection_style: SelectionStyle,
init_command: ?Command = null,
deinit_command: ?Command = null,
initialized: bool = false,
pub fn run_init(self: *Mode) void {
if (self.initialized) return;
self.initialized = true;
clear_integer_argument();
if (self.init_command) |init_command| init_command.execute_const();
}
pub fn deinit(self: *Mode) void { pub fn deinit(self: *Mode) void {
if (self.deinit_command) |deinit_command|
deinit_command.execute_const();
self.allocator.free(self.mode); self.allocator.free(self.mode);
self.input_handler.deinit(); self.input_handler.deinit();
if (self.event_handler) |eh| eh.deinit(); if (self.event_handler) |eh| eh.deinit();
@ -177,11 +138,19 @@ fn get_or_load_namespace(namespace_name: []const u8) LoadError!*const Namespace
pub fn set_namespace(namespace_name: []const u8) LoadError!void { pub fn set_namespace(namespace_name: []const u8) LoadError!void {
const new_namespace = try get_or_load_namespace(namespace_name); const new_namespace = try get_or_load_namespace(namespace_name);
if (globals.current_namespace) |old_namespace| if (globals.current_namespace) |old_namespace|
if (old_namespace.deinit_command) |deinit_command| if (old_namespace.deinit_command) |deinit|
deinit_command.execute_const(); deinit.execute_const() catch |e| {
const logger = log.logger("keybind");
logger.print_err("deinit_command", "ERROR: {s} {s}", .{ deinit.command, @errorName(e) });
logger.deinit();
};
globals.current_namespace = new_namespace; globals.current_namespace = new_namespace;
if (new_namespace.init_command) |init_command| if (new_namespace.init_command) |init|
init_command.execute_const(); init.execute_const() catch |e| {
const logger = log.logger("keybind");
logger.print_err("init_command", "ERROR: {s} {s}", .{ init.command, @errorName(e) });
logger.deinit();
};
} }
fn get_mode_binding_set(mode_name: []const u8, insert_command: []const u8) LoadError!*const BindingSet { fn get_mode_binding_set(mode_name: []const u8, insert_command: []const u8) LoadError!*const BindingSet {
@ -198,7 +167,7 @@ fn get_mode_binding_set(mode_name: []const u8, insert_command: []const u8) LoadE
return binding_set; return binding_set;
} }
pub const LoadError = (error{ NotFound, NotAnObject } || std.json.ParseError(std.json.Scanner) || parse_flow.ParseError || parse_vim.ParseError || std.json.ParseFromValueError); const LoadError = (error{ NotFound, NotAnObject } || std.json.ParseError(std.json.Scanner) || parse_flow.ParseError || parse_vim.ParseError || std.json.ParseFromValueError);
///A collection of modes that represent a switchable editor emulation ///A collection of modes that represent a switchable editor emulation
const Namespace = struct { const Namespace = struct {
@ -295,29 +264,13 @@ const Command = struct {
}; };
var buf: [2048]u8 = undefined; var buf: [2048]u8 = undefined;
@memcpy(buf[0..self.args.len], self.args); @memcpy(buf[0..self.args.len], self.args);
if (integer_argument) |int_arg| {
if (cbor.match(self.args, .{}) catch false and has_integer_argument(id)) {
integer_argument = null;
try command.execute(id, command.fmt(.{int_arg}));
return;
}
}
try command.execute(id, .{ .args = .{ .buf = buf[0..self.args.len] } }); try command.execute(id, .{ .args = .{ .buf = buf[0..self.args.len] } });
} }
fn execute_const(self: *const @This()) void { fn execute_const(self: *const @This()) !void {
var buf: [2048]u8 = undefined; var buf: [2048]u8 = undefined;
@memcpy(buf[0..self.args.len], self.args); @memcpy(buf[0..self.args.len], self.args);
command.executeName(self.command, .{ .args = .{ .buf = buf[0..self.args.len] } }) catch |e| { try command.executeName(self.command, .{ .args = .{ .buf = buf[0..self.args.len] } });
const logger = log.logger("keybind");
logger.print_err("init/deinit_command", "ERROR: {s} {s}", .{ self.command, @errorName(e) });
logger.deinit();
};
}
fn has_integer_argument(id: command.ID) bool {
const args = command.get_arguments(id) orelse return false;
return args.len == 1 and args[0] == .integer;
} }
fn load(allocator: std.mem.Allocator, tokens: []const std.json.Value) (parse_flow.ParseError || parse_vim.ParseError)!Command { fn load(allocator: std.mem.Allocator, tokens: []const std.json.Value) (parse_flow.ParseError || parse_vim.ParseError)!Command {
@ -420,8 +373,6 @@ const BindingSet = struct {
selection_style: SelectionStyle, selection_style: SelectionStyle,
insert_command: []const u8 = "", insert_command: []const u8 = "",
hints_map: KeybindHints = .{}, hints_map: KeybindHints = .{},
init_command: ?Command = null,
deinit_command: ?Command = null,
const KeySyntax = enum { flow, vim }; const KeySyntax = enum { flow, vim };
const OnMatchFailure = enum { insert, ignore }; const OnMatchFailure = enum { insert, ignore };
@ -440,8 +391,6 @@ const BindingSet = struct {
inherit: ?[]const u8 = null, inherit: ?[]const u8 = null,
inherits: ?[][]const u8 = null, inherits: ?[][]const u8 = null,
selection: ?SelectionStyle = null, selection: ?SelectionStyle = null,
init_command: ?[]const std.json.Value = null,
deinit_command: ?[]const std.json.Value = null,
}; };
const parsed = try std.json.parseFromValue(JsonConfig, allocator, mode_bindings, .{ const parsed = try std.json.parseFromValue(JsonConfig, allocator, mode_bindings, .{
.ignore_unknown_fields = true, .ignore_unknown_fields = true,
@ -453,8 +402,6 @@ const BindingSet = struct {
self.line_numbers = parsed.value.line_numbers; self.line_numbers = parsed.value.line_numbers;
self.cursor_shape = parsed.value.cursor; self.cursor_shape = parsed.value.cursor;
self.selection_style = parsed.value.selection orelse .normal; self.selection_style = parsed.value.selection orelse .normal;
if (parsed.value.init_command) |cmd| self.init_command = try Command.load(allocator, cmd);
if (parsed.value.deinit_command) |cmd| self.deinit_command = try Command.load(allocator, cmd);
try self.load_event(allocator, &self.press, input.event.press, parsed.value.press); try self.load_event(allocator, &self.press, input.event.press, parsed.value.press);
try self.load_event(allocator, &self.release, input.event.release, parsed.value.release); try self.load_event(allocator, &self.release, input.event.release, parsed.value.release);
if (parsed.value.inherits) |sibling_fallbacks| { if (parsed.value.inherits) |sibling_fallbacks| {
@ -769,14 +716,6 @@ pub fn current_key_event_sequence_fmt() KeyEventSequenceFmt {
return .{ .key_events = globals.current_sequence.items }; return .{ .key_events = globals.current_sequence.items };
} }
pub fn current_integer_argument() ?usize {
return integer_argument;
}
pub fn clear_integer_argument() void {
integer_argument = null;
}
const expectEqual = std.testing.expectEqual; const expectEqual = std.testing.expectEqual;
const parse_test_cases = .{ const parse_test_cases = .{

View file

@ -29,7 +29,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseErro
var iter = std.mem.tokenizeScalar(u8, item, '+'); var iter = std.mem.tokenizeScalar(u8, item, '+');
loop: while (iter.next()) |part| { loop: while (iter.next()) |part| {
if (part.len == 0) return parse_error("empty part in '{s}'", .{str}); if (part.len == 0) return parse_error("empty part in '{s}'", .{str});
const modsInfo = @typeInfo(input.ModSet).@"struct"; const modsInfo = @typeInfo(input.ModSet).Struct;
inline for (modsInfo.fields) |field| { inline for (modsInfo.fields) |field| {
if (std.mem.eql(u8, part, field.name)) { if (std.mem.eql(u8, part, field.name)) {
if (@field(mods, field.name)) return parse_error("duplicate modifier '{s}' in '{s}'", .{ part, str }); if (@field(mods, field.name)) return parse_error("duplicate modifier '{s}' in '{s}'", .{ part, str });

View file

@ -3,7 +3,7 @@ const syntax = @import("syntax");
const builtin = @import("builtin"); const builtin = @import("builtin");
const RGB = @import("color").RGB; const RGB = @import("color").RGB;
const bin_path = @import("bin_path"); const bin_path = @import("bin_path.zig");
const checkmark_width = if (builtin.os.tag != .windows) 2 else 3; const checkmark_width = if (builtin.os.tag != .windows) 2 else 3;

View file

@ -16,7 +16,7 @@ pub const Selection = struct {
end: Cursor = Cursor{}, end: Cursor = Cursor{},
}; };
pub fn create() error{ OutOfMemory, ThespianSpawnFailed }!Self { pub fn create() !Self {
return .{ .pid = try Process.create() }; return .{ .pid = try Process.create() };
} }
@ -45,7 +45,7 @@ const Process = struct {
selection: ?Selection = null, selection: ?Selection = null,
}; };
pub fn create() error{ OutOfMemory, ThespianSpawnFailed }!tp.pid { pub fn create() !tp.pid {
const self = try outer_a.create(Process); const self = try outer_a.create(Process);
self.* = .{ self.* = .{
.arena = std.heap.ArenaAllocator.init(outer_a), .arena = std.heap.ArenaAllocator.init(outer_a),
@ -123,13 +123,13 @@ const Process = struct {
if (isdupe(self.backwards.getLastOrNull(), entry)) { if (isdupe(self.backwards.getLastOrNull(), entry)) {
if (self.current) |current| self.forwards.append(current) catch {}; if (self.current) |current| self.forwards.append(current) catch {};
if (self.backwards.pop()) |top| const top = self.backwards.pop();
self.allocator.free(top.file_path); self.allocator.free(top.file_path);
tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "back", entry.file_path, entry.cursor.row, entry.cursor.col, self.backwards.items.len, self.forwards.items.len })); tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "back", entry.file_path, entry.cursor.row, entry.cursor.col, self.backwards.items.len, self.forwards.items.len }));
} else if (isdupe(self.forwards.getLastOrNull(), entry)) { } else if (isdupe(self.forwards.getLastOrNull(), entry)) {
if (self.current) |current| self.backwards.append(current) catch {}; if (self.current) |current| self.backwards.append(current) catch {};
if (self.forwards.pop()) |top| const top = self.forwards.pop();
self.allocator.free(top.file_path); self.allocator.free(top.file_path);
tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "forward", entry.file_path, entry.cursor.row, entry.cursor.col, self.backwards.items.len, self.forwards.items.len })); tp.trace(tp.channel.all, tp.message.fmt(.{ "location", "forward", entry.file_path, entry.cursor.row, entry.cursor.col, self.backwards.items.len, self.forwards.items.len }));
} else if (self.current) |current| { } else if (self.current) |current| {
try self.backwards.append(current); try self.backwards.append(current);

View file

@ -1,6 +1,8 @@
const std = @import("std"); const std = @import("std");
const tp = @import("thespian"); const tp = @import("thespian");
const fba = std.heap.FixedBufferAllocator;
const Self = @This(); const Self = @This();
pub const max_log_message = tp.max_message_size - 128; pub const max_log_message = tp.max_message_size - 128;
@ -9,7 +11,7 @@ allocator: std.mem.Allocator,
receiver: Receiver, receiver: Receiver,
subscriber: ?tp.pid, subscriber: ?tp.pid,
heap: [32 + 1024]u8, heap: [32 + 1024]u8,
fba: std.heap.FixedBufferAllocator, fba: fba,
msg_store: MsgStoreT, msg_store: MsgStoreT,
const MsgStoreT = std.DoublyLinkedList([]u8); const MsgStoreT = std.DoublyLinkedList([]u8);
@ -37,7 +39,7 @@ fn init(args: StartArgs) !*Self {
.receiver = Receiver.init(Self.receive, p), .receiver = Receiver.init(Self.receive, p),
.subscriber = null, .subscriber = null,
.heap = undefined, .heap = undefined,
.fba = std.heap.FixedBufferAllocator.init(&p.heap), .fba = fba.init(&p.heap),
.msg_store = MsgStoreT{}, .msg_store = MsgStoreT{},
}; };
return p; return p;

View file

@ -1,11 +1,10 @@
const std = @import("std"); const std = @import("std");
const tui = @import("tui"); const tui = @import("tui");
const cbor = @import("cbor");
const thespian = @import("thespian"); const thespian = @import("thespian");
const flags = @import("flags"); const flags = @import("flags");
const builtin = @import("builtin"); const builtin = @import("builtin");
const bin_path = @import("bin_path");
const bin_path = @import("bin_path.zig");
const list_languages = @import("list_languages.zig"); const list_languages = @import("list_languages.zig");
pub const file_link = @import("file_link.zig"); pub const file_link = @import("file_link.zig");
@ -16,7 +15,6 @@ const c = @cImport({
const build_options = @import("build_options"); const build_options = @import("build_options");
const log = @import("log"); const log = @import("log");
pub const version = @embedFile("version");
pub const version_info = @embedFile("version_info"); pub const version_info = @embedFile("version_info");
pub var max_diff_lines: usize = 50000; pub var max_diff_lines: usize = 50000;
@ -27,7 +25,7 @@ pub const application_title = "Flow Control";
pub const application_subtext = "a programmer's text editor"; pub const application_subtext = "a programmer's text editor";
pub const application_description = application_title ++ ": " ++ application_subtext; pub const application_description = application_title ++ ": " ++ application_subtext;
pub const std_options: std.Options = .{ pub const std_options = .{
// .log_level = if (builtin.mode == .Debug) .debug else .warn, // .log_level = if (builtin.mode == .Debug) .debug else .warn,
.log_level = if (builtin.mode == .Debug) .info else .warn, .log_level = if (builtin.mode == .Debug) .info else .warn,
.logFn = log.std_log_function, .logFn = log.std_log_function,
@ -35,11 +33,7 @@ pub const std_options: std.Options = .{
const renderer = @import("renderer"); const renderer = @import("renderer");
pub const panic = if (@hasDecl(renderer, "panic")) renderer.panic else default_panic; pub const panic = if (@hasDecl(renderer, "panic")) renderer.panic else std.builtin.default_panic;
fn default_panic(msg: []const u8, _: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
return std.debug.defaultPanic(msg, ret_addr);
}
pub fn main() anyerror!void { pub fn main() anyerror!void {
if (builtin.os.tag == .linux) { if (builtin.os.tag == .linux) {
@ -58,7 +52,7 @@ pub fn main() anyerror!void {
\\ \\
\\Pass in file names to be opened with an optional :LINE or :LINE:COL appended to the \\Pass in file names to be opened with an optional :LINE or :LINE:COL appended to the
\\file name to specify a specific location, or pass +<LINE> separately to set the line. \\file name to specify a specific location, or pass +<LINE> separately to set the line.
; ;
pub const descriptions = .{ pub const descriptions = .{
.project = "Set project directory (default: cwd)", .project = "Set project directory (default: cwd)",
@ -305,27 +299,7 @@ pub fn main() anyerror!void {
if (args.exec) |exec_str| { if (args.exec) |exec_str| {
var cmds = std.mem.splitScalar(u8, exec_str, ';'); var cmds = std.mem.splitScalar(u8, exec_str, ';');
while (cmds.next()) |cmd| { while (cmds.next()) |cmd| try tui_proc.send(.{ "cmd", cmd, .{} });
var count_args_ = std.mem.splitScalar(u8, cmd, ':');
var count: usize = 0;
while (count_args_.next()) |_| count += 1;
if (count == 0) break;
var msg = std.ArrayList(u8).init(a);
defer msg.deinit();
const writer = msg.writer();
var cmd_args = std.mem.splitScalar(u8, cmd, ':');
const cmd_ = cmd_args.next();
try cbor.writeArrayHeader(writer, 3);
try cbor.writeValue(writer, "cmd");
try cbor.writeValue(writer, cmd_);
try cbor.writeArrayHeader(writer, count - 1);
while (cmd_args.next()) |arg| try cbor.writeValue(writer, arg);
try tui_proc.send_raw(.{ .buf = msg.items });
}
} }
ctx.run(); ctx.run();
@ -369,6 +343,7 @@ fn trace_json(json: thespian.message.json_string_view) callconv(.C) void {
extern fn ___tracy_emit_message(txt: [*]const u8, size: usize, callstack: c_int) void; extern fn ___tracy_emit_message(txt: [*]const u8, size: usize, callstack: c_int) void;
fn trace_to_file(m: thespian.message.c_buffer_type) callconv(.C) void { fn trace_to_file(m: thespian.message.c_buffer_type) callconv(.C) void {
const cbor = @import("cbor");
const State = struct { const State = struct {
file: std.fs.File, file: std.fs.File,
last_time: i64, last_time: i64,
@ -466,6 +441,7 @@ fn read_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs: *[][]
} }
fn read_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void { fn read_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void {
const cbor = @import("cbor");
var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }); var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only });
defer file.close(); defer file.close();
const text = try file.readToEndAlloc(allocator, 64 * 1024); const text = try file.readToEndAlloc(allocator, 64 * 1024);
@ -501,6 +477,7 @@ fn read_text_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_:
} }
fn read_json_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void { fn read_json_config_file(T: type, allocator: std.mem.Allocator, conf: *T, bufs_: *[][]const u8, file_name: []const u8) !void {
const cbor = @import("cbor");
var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }); var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only });
defer file.close(); defer file.close();
const json = try file.readToEndAlloc(allocator, 64 * 1024); const json = try file.readToEndAlloc(allocator, 64 * 1024);
@ -521,6 +498,7 @@ fn read_cbor_config(
file_name: []const u8, file_name: []const u8,
cb: []const u8, cb: []const u8,
) !void { ) !void {
const cbor = @import("cbor");
var iter = cb; var iter = cb;
var field_name: []const u8 = undefined; var field_name: []const u8 = undefined;
while (cbor.matchString(&iter, &field_name) catch |e| switch (e) { while (cbor.matchString(&iter, &field_name) catch |e| switch (e) {
@ -528,7 +506,7 @@ fn read_cbor_config(
else => return e, else => return e,
}) { }) {
var known = false; var known = false;
inline for (@typeInfo(T).@"struct".fields) |field_info| inline for (@typeInfo(T).Struct.fields) |field_info|
if (comptime std.mem.eql(u8, "include_files", field_info.name)) { if (comptime std.mem.eql(u8, "include_files", field_info.name)) {
if (std.mem.eql(u8, field_name, field_info.name)) { if (std.mem.eql(u8, field_name, field_info.name)) {
known = true; known = true;
@ -569,9 +547,7 @@ fn read_nested_include_files(T: type, allocator: std.mem.Allocator, conf: *T, bu
}; };
} }
pub const ConfigWriteError = error{ CreateConfigFileFailed, WriteConfigFileFailed }; pub fn write_config(conf: anytype, allocator: std.mem.Allocator) !void {
pub fn write_config(conf: anytype, allocator: std.mem.Allocator) (ConfigDirError || ConfigWriteError)!void {
config_mutex.lock(); config_mutex.lock();
defer config_mutex.unlock(); defer config_mutex.unlock();
_ = allocator; _ = allocator;
@ -580,22 +556,16 @@ pub fn write_config(conf: anytype, allocator: std.mem.Allocator) (ConfigDirError
// return write_json_file(@TypeOf(conf), conf, allocator, try get_app_config_file_name(application_name, @typeName(@TypeOf(conf)))); // return write_json_file(@TypeOf(conf), conf, allocator, try get_app_config_file_name(application_name, @typeName(@TypeOf(conf))));
} }
fn write_text_config_file(comptime T: type, data: T, file_name: []const u8) ConfigWriteError!void { fn write_text_config_file(comptime T: type, data: T, file_name: []const u8) !void {
var file = std.fs.createFileAbsolute(file_name, .{ .truncate = true }) catch |e| { var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true });
std.log.err("createFileAbsolute failed with {any} for: {s}", .{ e, file_name });
return error.CreateConfigFileFailed;
};
defer file.close(); defer file.close();
const writer = file.writer(); const writer = file.writer();
write_config_to_writer(T, data, writer) catch |e| { return write_config_to_writer(T, data, writer);
std.log.err("write file failed with {any} for: {s}", .{ e, file_name });
return error.WriteConfigFileFailed;
};
} }
pub fn write_config_to_writer(comptime T: type, data: T, writer: anytype) @TypeOf(writer).Error!void { pub fn write_config_to_writer(comptime T: type, data: T, writer: anytype) !void {
const default: T = .{}; const default: T = .{};
inline for (@typeInfo(T).@"struct".fields) |field_info| { inline for (@typeInfo(T).Struct.fields) |field_info| {
if (config_eql( if (config_eql(
field_info.type, field_info.type,
@field(data, field_info.name), @field(data, field_info.name),
@ -617,8 +587,8 @@ fn config_eql(comptime T: type, a: T, b: T) bool {
else => {}, else => {},
} }
switch (@typeInfo(T)) { switch (@typeInfo(T)) {
.bool, .int, .float, .@"enum" => return a == b, .Bool, .Int, .Float, .Enum => return a == b,
.optional => |info| { .Optional => |info| {
if (a == null and b == null) if (a == null and b == null)
return true; return true;
if (a == null or b == null) if (a == null or b == null)
@ -631,6 +601,7 @@ fn config_eql(comptime T: type, a: T, b: T) bool {
} }
fn write_json_file(comptime T: type, data: T, allocator: std.mem.Allocator, file_name: []const u8) !void { fn write_json_file(comptime T: type, data: T, allocator: std.mem.Allocator, file_name: []const u8) !void {
const cbor = @import("cbor");
var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true }); var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true });
defer file.close(); defer file.close();
@ -671,47 +642,11 @@ pub fn list_keybind_namespaces(allocator: std.mem.Allocator) ![]const []const u8
return result.toOwnedSlice(); return result.toOwnedSlice();
} }
pub fn read_theme(allocator: std.mem.Allocator, theme_name: []const u8) ?[]const u8 {
const file_name = get_theme_file_name(theme_name) catch return null;
var file = std.fs.openFileAbsolute(file_name, .{ .mode = .read_only }) catch return null;
defer file.close();
return file.readToEndAlloc(allocator, 64 * 1024) catch null;
}
pub fn write_theme(theme_name: []const u8, content: []const u8) !void {
const file_name = try get_theme_file_name(theme_name);
var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true });
defer file.close();
return file.writeAll(content);
}
pub fn list_themes(allocator: std.mem.Allocator) ![]const []const u8 {
var dir = try std.fs.openDirAbsolute(try get_theme_directory(), .{ .iterate = true });
defer dir.close();
var result = std.ArrayList([]const u8).init(allocator);
var iter = dir.iterateAssumeFirstIteration();
while (try iter.next()) |entry| {
switch (entry.kind) {
.file, .sym_link => try result.append(try allocator.dupe(u8, std.fs.path.stem(entry.name))),
else => continue,
}
}
return result.toOwnedSlice();
}
pub fn get_config_dir() ![]const u8 { pub fn get_config_dir() ![]const u8 {
return get_app_config_dir(application_name); return get_app_config_dir(application_name);
} }
pub const ConfigDirError = error{ fn get_app_config_dir(appname: []const u8) ![]const u8 {
NoSpaceLeft,
MakeConfigDirFailed,
MakeHomeConfigDirFailed,
MakeAppConfigDirFailed,
AppConfigDirUnavailable,
};
fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
const a = std.heap.c_allocator; const a = std.heap.c_allocator;
const local = struct { const local = struct {
var config_dir_buffer: [std.posix.PATH_MAX]u8 = undefined; var config_dir_buffer: [std.posix.PATH_MAX]u8 = undefined;
@ -727,7 +662,7 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config", .{home}); const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config", .{home});
std.fs.makeDirAbsolute(dir) catch |e| switch (e) { std.fs.makeDirAbsolute(dir) catch |e| switch (e) {
error.PathAlreadyExists => {}, error.PathAlreadyExists => {},
else => return error.MakeHomeConfigDirFailed, else => return e,
}; };
break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config/{s}", .{ home, appname }); break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config/{s}", .{ home, appname });
} else if (builtin.os.tag == .windows) ret: { } else if (builtin.os.tag == .windows) ret: {
@ -736,7 +671,7 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/{s}", .{ appdata, appname }); const dir = try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/{s}", .{ appdata, appname });
std.fs.makeDirAbsolute(dir) catch |e| switch (e) { std.fs.makeDirAbsolute(dir) catch |e| switch (e) {
error.PathAlreadyExists => {}, error.PathAlreadyExists => {},
else => return error.MakeAppConfigDirFailed, else => return e,
}; };
break :ret dir; break :ret dir;
} else return error.AppConfigDirUnavailable; } else return error.AppConfigDirUnavailable;
@ -745,15 +680,12 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
local.config_dir = config_dir; local.config_dir = config_dir;
std.fs.makeDirAbsolute(config_dir) catch |e| switch (e) { std.fs.makeDirAbsolute(config_dir) catch |e| switch (e) {
error.PathAlreadyExists => {}, error.PathAlreadyExists => {},
else => return error.MakeConfigDirFailed, else => return e,
}; };
var keybind_dir_buffer: [std.posix.PATH_MAX]u8 = undefined; var keybind_dir_buffer: [std.posix.PATH_MAX]u8 = undefined;
std.fs.makeDirAbsolute(try std.fmt.bufPrint(&keybind_dir_buffer, "{s}/{s}", .{ config_dir, keybind_dir })) catch {}; std.fs.makeDirAbsolute(try std.fmt.bufPrint(&keybind_dir_buffer, "{s}/{s}", .{ config_dir, keybind_dir })) catch {};
var theme_dir_buffer: [std.posix.PATH_MAX]u8 = undefined;
std.fs.makeDirAbsolute(try std.fmt.bufPrint(&theme_dir_buffer, "{s}/{s}", .{ config_dir, theme_dir })) catch {};
return config_dir; return config_dir;
} }
@ -848,11 +780,11 @@ fn get_app_state_dir(appname: []const u8) ![]const u8 {
return state_dir; return state_dir;
} }
fn get_app_config_file_name(appname: []const u8, comptime base_name: []const u8) ConfigDirError![]const u8 { fn get_app_config_file_name(appname: []const u8, comptime base_name: []const u8) ![]const u8 {
return get_app_config_dir_file_name(appname, base_name ++ ".json"); return get_app_config_dir_file_name(appname, base_name ++ ".json");
} }
fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ConfigDirError![]const u8 { fn get_app_config_dir_file_name(appname: []const u8, comptime config_file_name: []const u8) ![]const u8 {
const local = struct { const local = struct {
var config_file_buffer: [std.posix.PATH_MAX]u8 = undefined; var config_file_buffer: [std.posix.PATH_MAX]u8 = undefined;
}; };
@ -899,28 +831,6 @@ pub fn get_keybind_namespace_file_name(namespace_name: []const u8) ![]const u8 {
return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}.json", .{ dir, namespace_name }); return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}.json", .{ dir, namespace_name });
} }
const theme_dir = "themes";
fn get_theme_directory() ![]const u8 {
const local = struct {
var dir_buffer: [std.posix.PATH_MAX]u8 = undefined;
};
const a = std.heap.c_allocator;
if (std.process.getEnvVarOwned(a, "FLOW_THEMES_DIR") catch null) |dir| {
defer a.free(dir);
return try std.fmt.bufPrint(&local.dir_buffer, "{s}", .{dir});
}
return try std.fmt.bufPrint(&local.dir_buffer, "{s}/{s}", .{ try get_app_config_dir(application_name), theme_dir });
}
pub fn get_theme_file_name(theme_name: []const u8) ![]const u8 {
const dir = try get_theme_directory();
const local = struct {
var file_buffer: [std.posix.PATH_MAX]u8 = undefined;
};
return try std.fmt.bufPrint(&local.file_buffer, "{s}/{s}.json", .{ dir, theme_name });
}
fn restart() noreturn { fn restart() noreturn {
var executable: [:0]const u8 = std.mem.span(std.os.argv[0]); var executable: [:0]const u8 = std.mem.span(std.os.argv[0]);
var is_basename = true; var is_basename = true;

View file

@ -232,13 +232,16 @@ pub fn update_mru(file_path: []const u8, row: usize, col: usize, ephemeral: bool
return send(.{ "update_mru", project, file_path, row, col }); return send(.{ "update_mru", project, file_path, row, col });
} }
pub fn get_mru_position(allocator: std.mem.Allocator, file_path: []const u8, ctx: anytype) (ProjectManagerError || ProjectError)!void { pub fn get_mru_position(allocator: std.mem.Allocator, file_path: []const u8) (ProjectManagerError || ProjectError || CallError || cbor.Error)!?Project.FilePos {
const frame = tracy.initZone(@src(), .{ .name = "get_mru_position" });
defer frame.deinit();
const project = tp.env.get().str("project"); const project = tp.env.get().str("project");
if (project.len == 0) if (project.len == 0)
return error.NoProject; return error.NoProject;
const rsp = try (try get()).pid.call(allocator, request_timeout, .{ "get_mru_position", project, file_path });
const cp = @import("completion.zig"); defer allocator.free(rsp.buf);
return cp.send(allocator, (try get()).pid, .{ "get_mru_position", project, file_path }, ctx); var pos: Project.FilePos = undefined;
return if (try cbor.match(rsp.buf, .{ tp.extract(&pos.row), tp.extract(&pos.col) })) pos else null;
} }
const Process = struct { const Process = struct {
@ -325,7 +328,6 @@ const Process = struct {
var text_len: usize = 0; var text_len: usize = 0;
var n: usize = 0; var n: usize = 0;
var task: []const u8 = undefined; var task: []const u8 = undefined;
var context: usize = undefined;
var root_dst: usize = 0; var root_dst: usize = 0;
var root_src: usize = 0; var root_src: usize = 0;
@ -342,9 +344,6 @@ const Process = struct {
if (self.walker) |pid| pid.deinit(); if (self.walker) |pid| pid.deinit();
self.walker = null; self.walker = null;
self.loaded(project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; self.loaded(project_directory) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "git", tp.extract(&context), tp.more })) {
const project: *Project = @ptrFromInt(context);
project.process_git(m) catch {};
} else if (try cbor.match(m.buf, .{ "update_mru", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) { } else if (try cbor.match(m.buf, .{ "update_mru", tp.extract(&project_directory), tp.extract(&path), tp.extract(&row), tp.extract(&col) })) {
self.update_mru(project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed; self.update_mru(project_directory, path, row, col) catch |e| return from.forward_error(e, @errorReturnTrace()) catch error.ClientFailed;
} else if (try cbor.match(m.buf, .{ "child", tp.extract(&project_directory), tp.extract(&language_server), "notify", tp.extract(&method), tp.extract_cbor(&params_cb) })) { } else if (try cbor.match(m.buf, .{ "child", tp.extract(&project_directory), tp.extract(&language_server), "notify", tp.extract(&method), tp.extract_cbor(&params_cb) })) {
@ -427,7 +426,6 @@ const Process = struct {
self.walker = try walk_tree_async(self.allocator, project_directory); self.walker = try walk_tree_async(self.allocator, project_directory);
self.restore_project(project) catch |e| self.logger.err("restore_project", e); self.restore_project(project) catch |e| self.logger.err("restore_project", e);
project.sort_files_by_mtime(); project.sort_files_by_mtime();
project.query_git();
} else { } else {
self.logger.print("switched to: {s}", .{project_directory}); self.logger.print("switched to: {s}", .{project_directory});
} }
@ -926,11 +924,10 @@ const FilteredWalker = struct {
var containing = top; var containing = top;
var dirname_len = top.dirname_len; var dirname_len = top.dirname_len;
if (top.iter.next() catch { if (top.iter.next() catch {
var item_ = self.stack.pop(); var item = self.stack.pop();
if (item_) |*item| if (self.stack.items.len != 0) {
if (self.stack.items.len != 0) { item.iter.dir.close();
item.iter.dir.close(); }
};
continue; continue;
}) |base| { }) |base| {
self.name_buffer.shrinkRetainingCapacity(dirname_len); self.name_buffer.shrinkRetainingCapacity(dirname_len);
@ -961,11 +958,10 @@ const FilteredWalker = struct {
else => continue, else => continue,
} }
} else { } else {
var item_ = self.stack.pop(); var item = self.stack.pop();
if (item_) |*item| if (self.stack.items.len != 0) {
if (self.stack.items.len != 0) { item.iter.dir.close();
item.iter.dir.close(); }
};
} }
} }
return null; return null;

View file

@ -76,16 +76,8 @@ pub fn columns(self: *const Cell) usize {
} }
pub fn dim(self: *Cell, alpha: u8) void { pub fn dim(self: *Cell, alpha: u8) void {
self.dim_fg(alpha);
self.dim_bg(alpha);
}
pub fn dim_bg(self: *Cell, alpha: u8) void {
self.cell.style.bg = apply_alpha_value(self.cell.style.bg, alpha);
}
pub fn dim_fg(self: *Cell, alpha: u8) void {
self.cell.style.fg = apply_alpha_value(self.cell.style.fg, alpha); self.cell.style.fg = apply_alpha_value(self.cell.style.fg, alpha);
self.cell.style.bg = apply_alpha_value(self.cell.style.bg, alpha);
} }
fn apply_alpha_value(c: vaxis.Cell.Color, a: u8) vaxis.Cell.Color { fn apply_alpha_value(c: vaxis.Cell.Color, a: u8) vaxis.Cell.Color {

View file

@ -37,7 +37,7 @@ pub const option = enum {
}; };
pub fn init(nopts: *const Options, parent_: Plane) !Plane { pub fn init(nopts: *const Options, parent_: Plane) !Plane {
const opts: vaxis.Window.ChildOptions = .{ const opts = .{
.x_off = @as(i17, @intCast(nopts.x)), .x_off = @as(i17, @intCast(nopts.x)),
.y_off = @as(i17, @intCast(nopts.y)), .y_off = @as(i17, @intCast(nopts.y)),
.width = @as(u16, @intCast(nopts.cols)), .width = @as(u16, @intCast(nopts.cols)),

View file

@ -2,15 +2,13 @@ const vaxis = @import("vaxis");
const meta = @import("std").meta; const meta = @import("std").meta;
const utf8Encode = @import("std").unicode.utf8Encode; const utf8Encode = @import("std").unicode.utf8Encode;
const utf8Decode = @import("std").unicode.utf8Decode;
const utf8ValidateSlice = @import("std").unicode.utf8ValidateSlice;
const FormatOptions = @import("std").fmt.FormatOptions; const FormatOptions = @import("std").fmt.FormatOptions;
pub const key = vaxis.Key; pub const key = vaxis.Key;
pub const Key = u21; pub const Key = u21;
pub const Mouse = vaxis.Mouse.Button; pub const Mouse = vaxis.Mouse.Button;
pub const MouseType = @typeInfo(Mouse).@"enum".tag_type; pub const MouseType = @typeInfo(Mouse).Enum.tag_type;
pub const mouse = struct { pub const mouse = struct {
pub const MOTION: Mouse = vaxis.Mouse.Button.none; pub const MOTION: Mouse = vaxis.Mouse.Button.none;
@ -76,7 +74,6 @@ pub const KeyEvent = struct {
} }
pub fn eql_unshifted(self: @This(), other: @This()) bool { pub fn eql_unshifted(self: @This(), other: @This()) bool {
if (self.text.len > 0 or other.text.len > 0) return false;
const self_mods = self.mods_no_caps(); const self_mods = self.mods_no_caps();
const other_mods = other.mods_no_caps(); const other_mods = other.mods_no_caps();
return self.key_unshifted == other.key_unshifted and self_mods == other_mods; return self.key_unshifted == other.key_unshifted and self_mods == other_mods;
@ -120,7 +117,7 @@ pub const KeyEvent = struct {
pub fn from_message( pub fn from_message(
event_: Event, event_: Event,
keypress_: Key, keypress_: Key,
keypress_shifted_: Key, keypress_shifted: Key,
text: []const u8, text: []const u8,
modifiers: Mods, modifiers: Mods,
) @This() { ) @This() {
@ -131,11 +128,6 @@ pub const KeyEvent = struct {
key.left_alt, key.right_alt => modifiers & ~mod.alt, key.left_alt, key.right_alt => modifiers & ~mod.alt,
else => modifiers, else => modifiers,
}; };
var keypress_shifted: Key = keypress_shifted_;
if (text.len > 0 and text.len < 5 and utf8ValidateSlice(text)) blk: {
keypress_shifted = utf8Decode(text) catch break :blk;
}
const keypress, const mods = if (keypress_shifted == keypress_) const keypress, const mods = if (keypress_shifted == keypress_)
map_key_to_unshifed_legacy(keypress_shifted, mods_) map_key_to_unshifed_legacy(keypress_shifted, mods_)
else else

View file

@ -12,7 +12,6 @@ pub const Cell = @import("Cell.zig");
pub const CursorShape = vaxis.Cell.CursorShape; pub const CursorShape = vaxis.Cell.CursorShape;
pub const style = @import("style.zig").StyleBits; pub const style = @import("style.zig").StyleBits;
pub const styles = @import("style.zig");
const Self = @This(); const Self = @This();
pub const log_name = "vaxis"; pub const log_name = "vaxis";
@ -26,7 +25,6 @@ no_alternate: bool,
event_buffer: std.ArrayList(u8), event_buffer: std.ArrayList(u8),
input_buffer: std.ArrayList(u8), input_buffer: std.ArrayList(u8),
mods: vaxis.Key.Modifiers = .{}, mods: vaxis.Key.Modifiers = .{},
queries_done: bool,
bracketed_paste: bool = false, bracketed_paste: bool = false,
bracketed_paste_buffer: std.ArrayList(u8), bracketed_paste_buffer: std.ArrayList(u8),
@ -41,25 +39,7 @@ logger: log.Logger,
loop: Loop, loop: Loop,
pub const Error = error{ pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool, _: *const fn (ctx: *anyopaque) void) !Self {
UnexpectedRendererEvent,
OutOfMemory,
IntegerTooLarge,
IntegerTooSmall,
InvalidType,
TooShort,
Utf8CannotEncodeSurrogateHalf,
CodepointTooLarge,
TtyInitError,
TtyWriteError,
InvalidFloatType,
InvalidArrayType,
InvalidPIntType,
JsonIncompatibleType,
NotAnObject,
} || std.Thread.SpawnError;
pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool, _: *const fn (ctx: *anyopaque) void) Error!Self {
const opts: vaxis.Vaxis.Options = .{ const opts: vaxis.Vaxis.Options = .{
.kitty_keyboard_flags = .{ .kitty_keyboard_flags = .{
.disambiguate = true, .disambiguate = true,
@ -72,7 +52,7 @@ pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate:
}; };
return .{ return .{
.allocator = allocator, .allocator = allocator,
.tty = vaxis.Tty.init() catch return error.TtyInitError, .tty = try vaxis.Tty.init(),
.vx = try vaxis.init(allocator, opts), .vx = try vaxis.init(allocator, opts),
.no_alternate = no_alternate, .no_alternate = no_alternate,
.event_buffer = std.ArrayList(u8).init(allocator), .event_buffer = std.ArrayList(u8).init(allocator),
@ -81,7 +61,6 @@ pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate:
.handler_ctx = handler_ctx, .handler_ctx = handler_ctx,
.logger = log.logger(log_name), .logger = log.logger(log_name),
.loop = undefined, .loop = undefined,
.queries_done = false,
}; };
} }
@ -101,29 +80,28 @@ var panic_cleanup: ?struct {
vx: *vaxis.Vaxis, vx: *vaxis.Vaxis,
} = null; } = null;
pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn { pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
_ = error_return_trace; // TODO: what to do with this in zig-0.14?
const cleanup = panic_cleanup; const cleanup = panic_cleanup;
panic_cleanup = null; panic_cleanup = null;
if (cleanup) |self| { if (cleanup) |self| {
self.vx.deinit(self.allocator, self.tty.anyWriter()); self.vx.deinit(self.allocator, self.tty.anyWriter());
self.tty.deinit(); self.tty.deinit();
} }
return std.debug.defaultPanic(msg, ret_addr orelse @returnAddress()); return std.builtin.default_panic(msg, error_return_trace, ret_addr orelse @returnAddress());
} }
pub fn run(self: *Self) Error!void { pub fn run(self: *Self) !void {
self.vx.sgr = .legacy; self.vx.sgr = .legacy;
self.vx.conpty_hacks = true; self.vx.conpty_hacks = true;
panic_cleanup = .{ .allocator = self.allocator, .tty = &self.tty, .vx = &self.vx }; panic_cleanup = .{ .allocator = self.allocator, .tty = &self.tty, .vx = &self.vx };
if (!self.no_alternate) self.vx.enterAltScreen(self.tty.anyWriter()) catch return error.TtyWriteError; if (!self.no_alternate) try self.vx.enterAltScreen(self.tty.anyWriter());
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
try self.resize(.{ .rows = 25, .cols = 80, .x_pixel = 0, .y_pixel = 0 }); // dummy resize to fully init vaxis try self.resize(.{ .rows = 25, .cols = 80, .x_pixel = 0, .y_pixel = 0 }); // dummy resize to fully init vaxis
} else { } else {
self.sigwinch() catch return error.TtyWriteError; try self.sigwinch();
} }
self.vx.setBracketedPaste(self.tty.anyWriter(), true) catch return error.TtyWriteError; try self.vx.setBracketedPaste(self.tty.anyWriter(), true);
self.vx.queryTerminalSend(self.tty.anyWriter()) catch return error.TtyWriteError; try self.vx.queryTerminalSend(self.tty.anyWriter());
self.loop = Loop.init(&self.tty, &self.vx); self.loop = Loop.init(&self.tty, &self.vx);
try self.loop.start(); try self.loop.start();
@ -140,8 +118,8 @@ pub fn sigwinch(self: *Self) !void {
try self.resize(try vaxis.Tty.getWinsize(self.input_fd_blocking())); try self.resize(try vaxis.Tty.getWinsize(self.input_fd_blocking()));
} }
fn resize(self: *Self, ws: vaxis.Winsize) error{ TtyWriteError, OutOfMemory }!void { fn resize(self: *Self, ws: vaxis.Winsize) !void {
self.vx.resize(self.allocator, self.tty.anyWriter(), ws) catch return error.TtyWriteError; try self.vx.resize(self.allocator, self.tty.anyWriter(), ws);
self.vx.queueRefresh(); self.vx.queueRefresh();
if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{"resize"})); if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{"resize"}));
} }
@ -165,7 +143,7 @@ pub fn input_fd_blocking(self: Self) i32 {
return self.tty.fd; return self.tty.fd;
} }
pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { pub fn process_renderer_event(self: *Self, msg: []const u8) !void {
var input_: []const u8 = undefined; var input_: []const u8 = undefined;
var text_: []const u8 = undefined; var text_: []const u8 = undefined;
if (!try cbor.match(msg, .{ "RDR", cbor.extract(&input_), cbor.extract(&text_) })) if (!try cbor.match(msg, .{ "RDR", cbor.extract(&input_), cbor.extract(&text_) }))
@ -174,15 +152,6 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void {
const event = std.mem.bytesAsValue(vaxis.Event, input_); const event = std.mem.bytesAsValue(vaxis.Event, input_);
switch (event.*) { switch (event.*) {
.key_press => |key__| { .key_press => |key__| {
// Check for a cursor position response for our explicity width query. This will
// always be an F3 key with shift = true, and we must be looking for queries
if (key__.codepoint == vaxis.Key.f3 and key__.mods.shift and !self.queries_done) {
self.logger.print("explicit width capability detected", .{});
self.vx.caps.explicit_width = true;
self.vx.caps.unicode = .unicode;
self.vx.screen.width_method = .unicode;
return;
}
const key_ = filter_mods(normalize_shifted_alphas(key__)); const key_ = filter_mods(normalize_shifted_alphas(key__));
try self.sync_mod_state(key_.codepoint, key_.mods); try self.sync_mod_state(key_.codepoint, key_.mods);
const cbor_msg = try self.fmtmsg(.{ const cbor_msg = try self.fmtmsg(.{
@ -284,9 +253,8 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void {
self.vx.caps.sgr_pixels = true; self.vx.caps.sgr_pixels = true;
}, },
.cap_da1 => { .cap_da1 => {
self.queries_done = true;
self.vx.enableDetectedFeatures(self.tty.anyWriter()) catch |e| self.logger.err("enable features", e); self.vx.enableDetectedFeatures(self.tty.anyWriter()) catch |e| self.logger.err("enable features", e);
self.vx.setMouseMode(self.tty.anyWriter(), true) catch return error.TtyWriteError; try self.vx.setMouseMode(self.tty.anyWriter(), true);
}, },
.cap_kitty_keyboard => { .cap_kitty_keyboard => {
self.logger.print("kitty keyboard capability detected", .{}); self.logger.print("kitty keyboard capability detected", .{});
@ -305,7 +273,7 @@ pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void {
} }
} }
fn fmtmsg(self: *Self, value: anytype) std.ArrayList(u8).Writer.Error![]const u8 { fn fmtmsg(self: *Self, value: anytype) ![]const u8 {
self.event_buffer.clearRetainingCapacity(); self.event_buffer.clearRetainingCapacity();
try cbor.writeValue(self.event_buffer.writer(), value); try cbor.writeValue(self.event_buffer.writer(), value);
return self.event_buffer.items; return self.event_buffer.items;
@ -347,7 +315,7 @@ fn handle_bracketed_paste_end(self: *Self) !void {
if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", self.bracketed_paste_buffer.items })); if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", self.bracketed_paste_buffer.items }));
} }
fn handle_bracketed_paste_error(self: *Self, e: Error) !void { fn handle_bracketed_paste_error(self: *Self, e: anytype) !void {
self.logger.err("bracketed paste", e); self.logger.err("bracketed paste", e);
self.bracketed_paste_buffer.clearAndFree(); self.bracketed_paste_buffer.clearAndFree();
self.bracketed_paste = false; self.bracketed_paste = false;
@ -526,7 +494,7 @@ const Loop = struct {
} }
/// spawns the input thread to read input from the tty /// spawns the input thread to read input from the tty
pub fn start(self: *Loop) std.Thread.SpawnError!void { pub fn start(self: *Loop) !void {
if (self.thread) |_| return; if (self.thread) |_| return;
self.thread = try std.Thread.spawn(.{}, Loop.ttyRun, .{self}); self.thread = try std.Thread.spawn(.{}, Loop.ttyRun, .{self});
} }

View file

@ -4,11 +4,11 @@ pub const StyleBits = packed struct(u5) {
undercurl: bool = false, undercurl: bool = false,
underline: bool = false, underline: bool = false,
italic: bool = false, italic: bool = false,
};
pub const struck: StyleBits = .{ .struck = true }; pub const struck: StyleBits = .{ .struck = true };
pub const bold: StyleBits = .{ .bold = true }; pub const bold: StyleBits = .{ .bold = true };
pub const undercurl: StyleBits = .{ .undercurl = true }; pub const undercurl: StyleBits = .{ .undercurl = true };
pub const underline: StyleBits = .{ .underline = true }; pub const underline: StyleBits = .{ .underline = true };
pub const italic: StyleBits = .{ .italic = true }; pub const italic: StyleBits = .{ .italic = true };
pub const normal: StyleBits = .{}; pub const normal: StyleBits = .{};
};

View file

@ -18,55 +18,8 @@ pub const StyleBits = @import("tuirenderer").style;
const gui = @import("gui"); const gui = @import("gui");
const DropWriter = gui.DropWriter; const DropWriter = gui.DropWriter;
pub const style = StyleBits; pub const style = StyleBits;
pub const styles = @import("tuirenderer").styles;
pub const Error = error{ pub const panic = win32.messageBoxThenPanic(.{ .title = "Flow Panic" });
UnexpectedRendererEvent,
OutOfMemory,
IntegerTooLarge,
IntegerTooSmall,
InvalidType,
TooShort,
Utf8CannotEncodeSurrogateHalf,
CodepointTooLarge,
VaxisResizeError,
InvalidFloatType,
InvalidArrayType,
InvalidPIntType,
JsonIncompatibleType,
NotAnObject,
} || std.Thread.SpawnError;
pub const panic = messageBoxThenPanic(.{ .title = "Flow Panic" });
threadlocal var thread_is_panicing = false;
fn messageBoxThenPanic(
opt: struct {
title: [:0]const u8,
style: win32.MESSAGEBOX_STYLE = .{ .ICONASTERISK = 1 },
// TODO: add option/logic to include the stacktrace in the messagebox
},
) std.builtin.PanicFn {
return struct {
pub fn panic(
msg: []const u8,
_: ?*std.builtin.StackTrace,
ret_addr: ?usize,
) noreturn {
if (!thread_is_panicing) {
thread_is_panicing = true;
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
const msg_z: [:0]const u8 = if (std.fmt.allocPrintZ(
arena.allocator(),
"{s}",
.{msg},
)) |msg_z| msg_z else |_| "failed allocate error message";
_ = win32.MessageBoxA(null, msg_z, opt.title, opt.style);
}
std.debug.defaultPanic(msg, ret_addr);
}
}.panic;
}
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
vx: vaxis.Vaxis, vx: vaxis.Vaxis,
@ -82,7 +35,7 @@ thread: ?std.Thread = null,
hwnd: ?win32.HWND = null, hwnd: ?win32.HWND = null,
title_buf: std.ArrayList(u16), title_buf: std.ArrayList(u16),
style_: ?Style = null, style: ?Style = null,
const global = struct { const global = struct {
var init_called: bool = false; var init_called: bool = false;
@ -97,7 +50,7 @@ pub fn init(
handler_ctx: *anyopaque, handler_ctx: *anyopaque,
no_alternate: bool, no_alternate: bool,
dispatch_initialized: *const fn (ctx: *anyopaque) void, dispatch_initialized: *const fn (ctx: *anyopaque) void,
) Error!Self { ) !Self {
std.debug.assert(!global.init_called); std.debug.assert(!global.init_called);
global.init_called = true; global.init_called = true;
@ -133,16 +86,16 @@ pub fn deinit(self: *Self) void {
self.title_buf.deinit(); self.title_buf.deinit();
} }
pub fn run(self: *Self) Error!void { pub fn run(self: *Self) !void {
if (self.thread) |_| return; if (self.thread) |_| return;
// dummy resize to fully init vaxis // dummy resize to fully init vaxis
const drop_writer = DropWriter{}; const drop_writer = DropWriter{};
self.vx.resize( try self.vx.resize(
self.allocator, self.allocator,
drop_writer.writer().any(), drop_writer.writer().any(),
.{ .rows = 25, .cols = 80, .x_pixel = 0, .y_pixel = 0 }, .{ .rows = 25, .cols = 80, .x_pixel = 0, .y_pixel = 0 },
) catch return error.VaxisResizeError; );
self.thread = try gui.start(); self.thread = try gui.start();
} }
@ -181,7 +134,7 @@ pub fn stdplane(self: *Self) Plane {
return plane; return plane;
} }
pub fn process_renderer_event(self: *Self, msg: []const u8) Error!void { pub fn process_renderer_event(self: *Self, msg: []const u8) !void {
const Input = struct { const Input = struct {
kind: u8, kind: u8,
codepoint: u21, codepoint: u21,
@ -391,12 +344,12 @@ fn update_window_title(self: *Self) void {
} }
pub fn set_terminal_style(self: *Self, style_: Style) void { pub fn set_terminal_style(self: *Self, style_: Style) void {
self.style_ = style_; self.style = style_;
self.update_window_style(); self.update_window_style();
} }
fn update_window_style(self: *Self) void { fn update_window_style(self: *Self) void {
const hwnd = self.hwnd orelse return; const hwnd = self.hwnd orelse return;
if (self.style_) |style_| { if (self.style) |style_| {
if (style_.bg) |color| gui.set_window_background(hwnd, @intCast(color.color)); if (style_.bg) |color| gui.set_window_background(hwnd, @intCast(color.color));
} }
} }

View file

@ -134,7 +134,7 @@ const Process = struct {
fn receive(self: *Process, _: tp.pid_ref, m: tp.message) tp.result { fn receive(self: *Process, _: tp.pid_ref, m: tp.message) tp.result {
errdefer self.deinit(); errdefer self.deinit();
var bytes: []const u8 = ""; var bytes: []u8 = "";
if (try m.match(.{ "input", tp.extract(&bytes) })) { if (try m.match(.{ "input", tp.extract(&bytes) })) {
const sp = self.sp orelse return tp.exit_error(error.Closed, null); const sp = self.sp orelse return tp.exit_error(error.Closed, null);
@ -155,7 +155,7 @@ const Process = struct {
} }
} }
fn handle_output(self: *Process, bytes: []const u8) !void { fn handle_output(self: *Process, bytes: []u8) !void {
try self.output.appendSlice(bytes); try self.output.appendSlice(bytes);
} }

View file

@ -21,11 +21,6 @@ pub const Error = error{
IntegerTooSmall, IntegerTooSmall,
InvalidType, InvalidType,
TooShort, TooShort,
InvalidFloatType,
InvalidArrayType,
InvalidPIntType,
JsonIncompatibleType,
NotAnObject,
}; };
pub const OutputHandler = fn (context: usize, parent: tp.pid_ref, arg0: []const u8, output: []const u8) void; pub const OutputHandler = fn (context: usize, parent: tp.pid_ref, arg0: []const u8, output: []const u8) void;
@ -36,7 +31,6 @@ pub const Handlers = struct {
out: *const OutputHandler, out: *const OutputHandler,
err: ?*const OutputHandler = null, err: ?*const OutputHandler = null,
exit: *const ExitHandler = log_exit_handler, exit: *const ExitHandler = log_exit_handler,
log_execute: bool = true,
}; };
pub fn execute(allocator: std.mem.Allocator, argv: tp.message, handlers: Handlers) Error!void { pub fn execute(allocator: std.mem.Allocator, argv: tp.message, handlers: Handlers) Error!void {
@ -96,7 +90,6 @@ pub fn log_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, output:
_ = parent; _ = parent;
_ = arg0; _ = arg0;
const logger = log.logger(@typeName(Self)); const logger = log.logger(@typeName(Self));
defer logger.deinit();
var it = std.mem.splitScalar(u8, output, '\n'); var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |line| if (line.len > 0) logger.print("{s}", .{line}); while (it.next()) |line| if (line.len > 0) logger.print("{s}", .{line});
} }
@ -105,7 +98,6 @@ pub fn log_err_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, out
_ = context; _ = context;
_ = parent; _ = parent;
const logger = log.logger(@typeName(Self)); const logger = log.logger(@typeName(Self));
defer logger.deinit();
var it = std.mem.splitScalar(u8, output, '\n'); var it = std.mem.splitScalar(u8, output, '\n');
while (it.next()) |line| logger.print_err(arg0, "{s}", .{line}); while (it.next()) |line| logger.print_err(arg0, "{s}", .{line});
} }
@ -114,7 +106,6 @@ pub fn log_exit_handler(context: usize, parent: tp.pid_ref, arg0: []const u8, er
_ = context; _ = context;
_ = parent; _ = parent;
const logger = log.logger(@typeName(Self)); const logger = log.logger(@typeName(Self));
defer logger.deinit();
if (exit_code > 0) { if (exit_code > 0) {
logger.print_err(arg0, "'{s}' terminated {s} exitcode: {d}", .{ arg0, err_msg, exit_code }); logger.print_err(arg0, "'{s}' terminated {s} exitcode: {d}", .{ arg0, err_msg, exit_code });
} else { } else {
@ -126,7 +117,6 @@ pub fn log_exit_err_handler(context: usize, parent: tp.pid_ref, arg0: []const u8
_ = context; _ = context;
_ = parent; _ = parent;
const logger = log.logger(@typeName(Self)); const logger = log.logger(@typeName(Self));
defer logger.deinit();
if (exit_code > 0) { if (exit_code > 0) {
logger.print_err(arg0, "'{s}' terminated {s} exitcode: {d}", .{ arg0, err_msg, exit_code }); logger.print_err(arg0, "'{s}' terminated {s} exitcode: {d}", .{ arg0, err_msg, exit_code });
} }
@ -190,15 +180,14 @@ const Process = struct {
_ = tp.set_trap(true); _ = tp.set_trap(true);
var buf: [1024]u8 = undefined; var buf: [1024]u8 = undefined;
const json = self.argv.to_json(&buf) catch |e| return tp.exit_error(e, @errorReturnTrace()); const json = self.argv.to_json(&buf) catch |e| return tp.exit_error(e, @errorReturnTrace());
if (self.handlers.log_execute) self.logger.print("shell: execute {s}", .{json});
self.logger.print("shell: execute {s}", .{json});
self.sp = tp.subprocess.init(self.allocator, self.argv, module_name, self.stdin_behavior) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.sp = tp.subprocess.init(self.allocator, self.argv, module_name, self.stdin_behavior) catch |e| return tp.exit_error(e, @errorReturnTrace());
tp.receive(&self.receiver); tp.receive(&self.receiver);
} }
fn receive(self: *Process, _: tp.pid_ref, m: tp.message) tp.result { fn receive(self: *Process, _: tp.pid_ref, m: tp.message) tp.result {
errdefer self.deinit(); errdefer self.deinit();
var bytes: []const u8 = ""; var bytes: []u8 = "";
if (try m.match(.{ "input", tp.extract(&bytes) })) { if (try m.match(.{ "input", tp.extract(&bytes) })) {
const sp = self.sp orelse return tp.exit_error(error.Closed, null); const sp = self.sp orelse return tp.exit_error(error.Closed, null);

View file

@ -9,139 +9,119 @@ pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const tree_sitter_dep = b.dependency("tree_sitter", .{ const tree_sitter_dep = b.dependency("tree-sitter", .{
.target = target, .target = target,
.optimize = optimize, .optimize = optimize,
}); });
const tree_sitter_host_dep = b.dependency("tree_sitter", .{ const imports: []const std.Build.Module.Import = if (use_tree_sitter) &.{
.target = b.graph.host, .{ .name = "build_options", .module = options_mod },
.optimize = optimize, .{ .name = "treez", .module = tree_sitter_dep.module("treez") },
}); ts_queryfile(b, tree_sitter_dep, "queries/cmake/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-agda/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-astro/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-bash/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-c-sharp/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-c/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-cpp/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-css/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-diff/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-dockerfile/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-elixir/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-git-rebase/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-gitcommit/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-gleam/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-go/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-fish/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-haskell/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-hare/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-html/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-java/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-javascript/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-jsdoc/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-json/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-julia/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-kdl/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-lua/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-mail/queries/mail/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-make/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-markdown/tree-sitter-markdown/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-markdown/tree-sitter-markdown-inline/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-nasm/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-nim/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-ninja/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-nix/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-nu/queries/nu/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-ocaml/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-odin/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-openscad/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-org/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-php/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-python/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-purescript/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-regex/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-ruby/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-rust/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-ssh-config/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-scala/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-scheme/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-superhtml/tree-sitter-superhtml/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-sql/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-swift/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-toml/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-typescript/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-typst/queries/typst/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-vim/queries/vim/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-xml/queries/dtd/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-xml/queries/xml/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-yaml/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-zig/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-ziggy/tree-sitter-ziggy/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-ziggy/tree-sitter-ziggy-schema/queries/highlights.scm"),
ts_queryfile(b, tree_sitter_dep, "nvim-treesitter/queries/verilog/highlights.scm"),
const cbor_dep = b.dependency("cbor", .{ ts_queryfile(b, tree_sitter_dep, "queries/cmake/injections.scm"),
.target = target, ts_queryfile(b, tree_sitter_dep, "tree-sitter-astro/queries/injections.scm"),
.optimize = optimize, ts_queryfile(b, tree_sitter_dep, "tree-sitter-cpp/queries/injections.scm"),
}); ts_queryfile(b, tree_sitter_dep, "tree-sitter-elixir/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-gitcommit/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-hare/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-html/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-javascript/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-kdl/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-lua/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-markdown/tree-sitter-markdown-inline/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-markdown/tree-sitter-markdown/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-nasm/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-nix/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-nu/queries/nu/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-odin/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-openscad/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-php/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-purescript/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-purescript/vim_queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-rust/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-superhtml/tree-sitter-superhtml/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-swift/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-typst/queries/typst/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-vim/queries/vim/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-zig/queries/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "nvim-treesitter/queries/verilog/injections.scm"),
} else &.{
.{ .name = "build_options", .module = options_mod },
};
const ts_bin_query_gen = b.addExecutable(.{ _ = b.addModule("syntax", .{
.name = "ts_bin_query_gen",
.target = b.graph.host,
.root_source_file = b.path("src/ts_bin_query_gen.zig"),
});
ts_bin_query_gen.linkLibC();
ts_bin_query_gen.root_module.addImport("cbor", cbor_dep.module("cbor"));
ts_bin_query_gen.root_module.addImport("treez", tree_sitter_host_dep.module("treez"));
ts_bin_query_gen.root_module.addImport("build_options", options_mod);
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "queries/cmake/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-agda/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-astro/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-bash/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-c-sharp/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-c/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-cpp/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-css/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-diff/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-dockerfile/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-elixir/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-git-rebase/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-gitcommit/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-gleam/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-go/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-fish/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-haskell/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-hare/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-html/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-java/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-javascript/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-jsdoc/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-json/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-julia/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-kdl/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-lua/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-mail/queries/mail/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-make/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-markdown/tree-sitter-markdown/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-markdown/tree-sitter-markdown-inline/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nasm/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nim/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ninja/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nix/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nu/queries/nu/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ocaml/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-odin/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-openscad/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-org/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-php/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-python/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-purescript/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-regex/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ruby/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-rust/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ssh-config/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-scala/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-scheme/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-superhtml/tree-sitter-superhtml/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-sql/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-swift/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-toml/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-typescript/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-typst/queries/typst/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-vim/queries/vim/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-xml/queries/dtd/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-xml/queries/xml/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-yaml/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-zig/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ziggy/tree-sitter-ziggy/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-ziggy/tree-sitter-ziggy-schema/queries/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "nvim-treesitter/queries/verilog/highlights.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "queries/cmake/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-astro/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-cpp/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-elixir/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-gitcommit/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-hare/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-html/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-javascript/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-kdl/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-lua/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-markdown/tree-sitter-markdown-inline/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-markdown/tree-sitter-markdown/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nasm/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nix/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-nu/queries/nu/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-odin/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-openscad/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-php/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-purescript/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-purescript/vim_queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-rust/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-superhtml/tree-sitter-superhtml/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-swift/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-typst/queries/typst/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-vim/queries/vim/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "tree-sitter-zig/queries/injections.scm");
ts_queryfile(b, tree_sitter_dep, ts_bin_query_gen, "nvim-treesitter/queries/verilog/injections.scm");
const syntax_mod = b.addModule("syntax", .{
.root_source_file = b.path("src/syntax.zig"), .root_source_file = b.path("src/syntax.zig"),
.imports = &.{ .imports = imports,
.{ .name = "build_options", .module = options_mod },
.{ .name = "cbor", .module = cbor_dep.module("cbor") },
.{ .name = "treez", .module = tree_sitter_dep.module("treez") },
},
}); });
if (use_tree_sitter) {
const ts_bin_query_gen_step = b.addRunArtifact(ts_bin_query_gen);
const output = ts_bin_query_gen_step.addOutputFileArg("bin_queries.cbor");
syntax_mod.addAnonymousImport("syntax_bin_queries", .{ .root_source_file = output });
}
} }
fn ts_queryfile(b: *std.Build, dep: *std.Build.Dependency, bin_gen: *std.Build.Step.Compile, comptime sub_path: []const u8) void { fn ts_queryfile(b: *std.Build, dep: *std.Build.Dependency, comptime sub_path: []const u8) std.Build.Module.Import {
const module = b.createModule(.{ .root_source_file = dep.path(sub_path) }); return .{
bin_gen.root_module.addImport(sub_path, module); .name = sub_path,
.module = b.createModule(.{
.root_source_file = dep.path(sub_path),
}),
};
} }

View file

@ -1,17 +1,11 @@
.{ .{
.name = .flow_syntax, .name = "flow-syntax",
.version = "0.1.0", .version = "0.0.1",
.fingerprint = 0x3ba2584ea1cec85f,
.minimum_zig_version = "0.14.0-dev.3451+d8d2aa9af",
.dependencies = .{ .dependencies = .{
.tree_sitter = .{ .@"tree-sitter" = .{
.url = "https://github.com/neurocyte/tree-sitter/releases/download/master-86dd4d2536f2748c5b4ea0e1e70678039a569aac/source.tar.gz", .url = "https://github.com/neurocyte/tree-sitter/releases/download/master-86dd4d2536f2748c5b4ea0e1e70678039a569aac/source.tar.gz",
.hash = "N-V-__8AACablCbp-6lsRoKDEp6Xd2dHLe4AsW81blkSQxzs", .hash = "1220e9fba96c468283129e977767472dee00b16f356e5912431cec8f1a009b6691a2",
},
.cbor = .{
.url = "https://github.com/neurocyte/cbor/archive/1fccb83c70cd84e1dff57cc53f7db8fb99909a94.tar.gz",
.hash = "cbor-1.0.0-RcQE_HvqAACcrLH7t3IDZOshgY2xqJA_UX330MvwSepb",
}, },
}, },
.paths = .{ .paths = .{

View file

@ -1,187 +0,0 @@
const std = @import("std");
const build_options = @import("build_options");
const treez = if (build_options.use_tree_sitter)
@import("treez")
else
@import("treez_dummy.zig");
const Self = @This();
pub const tss = @import("ts_serializer.zig");
pub const FileType = @import("file_type.zig");
const Query = treez.Query;
allocator: std.mem.Allocator,
mutex: ?std.Thread.Mutex,
highlights: std.StringHashMapUnmanaged(*CacheEntry) = .{},
injections: std.StringHashMapUnmanaged(*CacheEntry) = .{},
ref_count: usize = 1,
const CacheEntry = struct {
mutex: ?std.Thread.Mutex,
query: ?*Query,
query_arena: ?*std.heap.ArenaAllocator,
query_type: QueryType,
file_type: *const FileType,
fn destroy(self: *@This(), allocator: std.mem.Allocator) void {
if (self.query_arena) |a| {
a.deinit();
allocator.destroy(a);
} else if (self.query) |q|
q.destroy();
self.query_arena = null;
self.query = null;
}
};
pub const QueryType = enum {
highlights,
injections,
};
const QueryParseError = error{
InvalidSyntax,
InvalidNodeType,
InvalidField,
InvalidCapture,
InvalidStructure,
InvalidLanguage,
};
const CacheError = error{
NotFound,
OutOfMemory,
};
pub const Error = CacheError || QueryParseError || QuerySerializeError;
pub fn create(allocator: std.mem.Allocator, opts: struct { lock: bool = false }) !*Self {
const self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.mutex = if (opts.lock) .{} else null,
};
return self;
}
pub fn deinit(self: *Self) void {
self.release_ref_unlocked_and_maybe_destroy();
}
fn add_ref_locked(self: *Self) void {
std.debug.assert(self.ref_count > 0);
self.ref_count += 1;
}
fn release_ref_unlocked_and_maybe_destroy(self: *Self) void {
{
if (self.mutex) |*mtx| mtx.lock();
defer if (self.mutex) |*mtx| mtx.unlock();
self.ref_count -= 1;
if (self.ref_count > 0) return;
}
var iter_highlights = self.highlights.iterator();
while (iter_highlights.next()) |p| {
self.allocator.free(p.key_ptr.*);
p.value_ptr.*.destroy(self.allocator);
self.allocator.destroy(p.value_ptr.*);
}
var iter_injections = self.injections.iterator();
while (iter_injections.next()) |p| {
self.allocator.free(p.key_ptr.*);
p.value_ptr.*.destroy(self.allocator);
self.allocator.destroy(p.value_ptr.*);
}
self.highlights.deinit(self.allocator);
self.injections.deinit(self.allocator);
self.allocator.destroy(self);
}
fn get_cache_entry(self: *Self, file_type: *const FileType, comptime query_type: QueryType) CacheError!*CacheEntry {
if (self.mutex) |*mtx| mtx.lock();
defer if (self.mutex) |*mtx| mtx.unlock();
const hash = switch (query_type) {
.highlights => &self.highlights,
.injections => &self.injections,
};
return if (hash.get(file_type.name)) |entry| entry else blk: {
const entry_ = try hash.getOrPut(self.allocator, try self.allocator.dupe(u8, file_type.name));
const q = try self.allocator.create(CacheEntry);
q.* = .{
.query = null,
.query_arena = null,
.mutex = if (self.mutex) |_| .{} else null,
.file_type = file_type,
.query_type = query_type,
};
entry_.value_ptr.* = q;
break :blk q;
};
}
fn get_cached_query(self: *Self, entry: *CacheEntry) Error!?*Query {
if (entry.mutex) |*mtx| mtx.lock();
defer if (entry.mutex) |*mtx| mtx.unlock();
return if (entry.query) |query| query else blk: {
const lang = entry.file_type.lang_fn() orelse std.debug.panic("tree-sitter parser function failed for language: {s}", .{entry.file_type.name});
const queries = FileType.queries.get(entry.file_type.name) orelse return null;
const query_bin = switch (entry.query_type) {
.highlights => queries.highlights_bin,
.injections => queries.injections_bin orelse return null,
};
const query, const arena = try deserialize_query(query_bin, lang, self.allocator);
entry.query = query;
entry.query_arena = arena;
break :blk entry.query.?;
};
}
fn pre_load_internal(self: *Self, file_type: *const FileType, comptime query_type: QueryType) Error!void {
_ = try self.get_cached_query(try self.get_cache_entry(file_type, query_type));
}
pub fn pre_load(self: *Self, lang_name: []const u8) Error!void {
const file_type = FileType.get_by_name(lang_name) orelse return;
_ = try self.pre_load_internal(file_type, .highlights);
_ = try self.pre_load_internal(file_type, .injections);
}
fn ReturnType(comptime query_type: QueryType) type {
return switch (query_type) {
.highlights => *Query,
.injections => ?*Query,
};
}
pub fn get(self: *Self, file_type: *const FileType, comptime query_type: QueryType) Error!ReturnType(query_type) {
const query = try self.get_cached_query(try self.get_cache_entry(file_type, query_type));
self.add_ref_locked();
return switch (@typeInfo(ReturnType(query_type))) {
.optional => |_| query,
else => query.?,
};
}
pub fn release(self: *Self, query: *Query, comptime query_type: QueryType) void {
_ = query;
_ = query_type;
self.release_ref_unlocked_and_maybe_destroy();
}
pub const QuerySerializeError = (tss.SerializeError || tss.DeserializeError);
fn deserialize_query(query_bin: []const u8, language: ?*const treez.Language, allocator: std.mem.Allocator) QuerySerializeError!struct { *Query, *std.heap.ArenaAllocator } {
var ts_query_out, const arena = try tss.fromCbor(query_bin, allocator);
ts_query_out.language = @intFromPtr(language);
const query_out: *Query = @alignCast(@ptrCast(ts_query_out));
return .{ query_out, arena };
}

View file

@ -1,5 +1,4 @@
const std = @import("std"); const std = @import("std");
const cbor = @import("cbor");
const build_options = @import("build_options"); const build_options = @import("build_options");
const treez = if (build_options.use_tree_sitter) const treez = if (build_options.use_tree_sitter)
@ -15,6 +14,8 @@ name: []const u8,
description: []const u8, description: []const u8,
lang_fn: LangFn, lang_fn: LangFn,
extensions: []const []const u8, extensions: []const []const u8,
highlights: [:0]const u8,
injections: ?[:0]const u8,
first_line_matches: ?FirstLineMatch = null, first_line_matches: ?FirstLineMatch = null,
comment: []const u8, comment: []const u8,
formatter: ?[]const []const u8, formatter: ?[]const []const u8,
@ -87,7 +88,7 @@ fn ft_func_name(comptime lang: []const u8) []const u8 {
const LangFn = *const fn () callconv(.C) ?*const treez.Language; const LangFn = *const fn () callconv(.C) ?*const treez.Language;
pub const FirstLineMatch = struct { const FirstLineMatch = struct {
prefix: ?[]const u8 = null, prefix: ?[]const u8 = null,
content: ?[]const u8 = null, content: ?[]const u8 = null,
}; };
@ -104,7 +105,7 @@ fn vec(comptime args: anytype) []const []const u8 {
fn load_file_types(comptime Namespace: type) []const FileType { fn load_file_types(comptime Namespace: type) []const FileType {
comptime switch (@typeInfo(Namespace)) { comptime switch (@typeInfo(Namespace)) {
.@"struct" => |info| { .Struct => |info| {
var count = 0; var count = 0;
for (info.decls) |_| { for (info.decls) |_| {
// @compileLog(decl.name, @TypeOf(@field(Namespace, decl.name))); // @compileLog(decl.name, @TypeOf(@field(Namespace, decl.name)));
@ -123,6 +124,22 @@ fn load_file_types(comptime Namespace: type) []const FileType {
.lang_fn = if (@hasField(@TypeOf(args), "parser")) args.parser else get_parser(lang), .lang_fn = if (@hasField(@TypeOf(args), "parser")) args.parser else get_parser(lang),
.extensions = vec(args.extensions), .extensions = vec(args.extensions),
.comment = args.comment, .comment = args.comment,
.highlights = if (build_options.use_tree_sitter)
if (@hasField(@TypeOf(args), "highlights"))
@embedFile(args.highlights)
else if (@hasField(@TypeOf(args), "highlights_list"))
@embedFile(args.highlights_list[0]) ++ "\n" ++ @embedFile(args.highlights_list[1])
else
@embedFile("tree-sitter-" ++ lang ++ "/queries/highlights.scm")
else
"",
.injections = if (build_options.use_tree_sitter)
if (@hasField(@TypeOf(args), "injections"))
@embedFile(args.injections)
else
null
else
null,
.first_line_matches = if (@hasField(@TypeOf(args), "first_line_matches")) args.first_line_matches else null, .first_line_matches = if (@hasField(@TypeOf(args), "first_line_matches")) args.first_line_matches else null,
.formatter = if (@hasField(@TypeOf(args), "formatter")) vec(args.formatter) else null, .formatter = if (@hasField(@TypeOf(args), "formatter")) vec(args.formatter) else null,
.language_server = if (@hasField(@TypeOf(args), "language_server")) vec(args.language_server) else null, .language_server = if (@hasField(@TypeOf(args), "language_server")) vec(args.language_server) else null,
@ -135,63 +152,3 @@ fn load_file_types(comptime Namespace: type) []const FileType {
else => @compileError("expected tuple or struct type"), else => @compileError("expected tuple or struct type"),
}; };
} }
pub const FileTypeQueries = struct {
highlights_bin: []const u8,
injections_bin: ?[]const u8,
};
pub const queries = std.static_string_map.StaticStringMap(FileTypeQueries).initComptime(load_queries());
fn load_queries() []const struct { []const u8, FileTypeQueries } {
if (!build_options.use_tree_sitter) return &.{};
@setEvalBranchQuota(16000);
const queries_cb = @embedFile("syntax_bin_queries");
var iter: []const u8 = queries_cb;
var len = cbor.decodeMapHeader(&iter) catch |e| {
@compileLog("cbor.decodeMapHeader", e);
@compileError("invalid syntax_bin_queries");
};
var construct_types: [len]struct { []const u8, FileTypeQueries } = undefined;
var i = 0;
while (len > 0) : (len -= 1) {
var lang: []const u8 = undefined;
if (!try cbor.matchString(&iter, &lang))
@compileError("invalid language name field");
construct_types[i] = .{ lang, .{
.highlights_bin = blk: {
var iter_: []const u8 = iter;
break :blk get_query_value_bin(&iter_, "highlights") orelse @compileError("missing highlights for " ++ lang);
},
.injections_bin = blk: {
var iter_: []const u8 = iter;
break :blk get_query_value_bin(&iter_, "injections");
},
} };
try cbor.skipValue(&iter);
i += 1;
}
const types = construct_types;
return &types;
}
fn get_query_value_bin(iter: *[]const u8, comptime query: []const u8) ?[]const u8 {
var len = cbor.decodeMapHeader(iter) catch |e| {
@compileLog("cbor.decodeMapHeader", e);
@compileError("invalid query map in syntax_bin_queries");
};
while (len > 0) : (len -= 1) {
var query_name: []const u8 = undefined;
if (!try cbor.matchString(iter, &query_name))
@compileError("invalid query name field");
if (std.mem.eql(u8, query_name, query)) {
var query_value: []const u8 = undefined;
if (try cbor.matchValue(iter, cbor.extract(&query_value)))
return query_value;
@compileError("invalid query value field");
} else {
try cbor.skipValue(iter);
}
}
return null;
}

View file

@ -1,6 +1,3 @@
const file_type = @import("file_type.zig");
const FirstLineMatch = file_type.FirstLineMatch;
pub const agda = .{ pub const agda = .{
.description = "Agda", .description = "Agda",
.extensions = .{"agda"}, .extensions = .{"agda"},
@ -21,7 +18,7 @@ pub const bash = .{
.icon = "󱆃", .icon = "󱆃",
.extensions = .{ "sh", "bash", ".profile" }, .extensions = .{ "sh", "bash", ".profile" },
.comment = "#", .comment = "#",
.first_line_matches = FirstLineMatch{ .prefix = "#!", .content = "sh" }, .first_line_matches = .{ .prefix = "#!", .content = "sh" },
.formatter = .{ "shfmt", "--indent", "4" }, .formatter = .{ "shfmt", "--indent", "4" },
.language_server = .{ "bash-language-server", "start" }, .language_server = .{ "bash-language-server", "start" },
}; };
@ -48,7 +45,7 @@ pub const conf = .{
.description = "Config", .description = "Config",
.color = 0x000000, .color = 0x000000,
.icon = "", .icon = "",
.extensions = .{ "conf", "log", "config", ".gitconfig", "gui_config" }, .extensions = .{ "conf", "config", ".gitconfig", "gui_config" },
.highlights = fish.highlights, .highlights = fish.highlights,
.comment = "#", .comment = "#",
.parser = fish.parser, .parser = fish.parser,
@ -256,7 +253,7 @@ pub const lua = .{
.extensions = .{"lua"}, .extensions = .{"lua"},
.comment = "--", .comment = "--",
.injections = "tree-sitter-lua/queries/injections.scm", .injections = "tree-sitter-lua/queries/injections.scm",
.first_line_matches = FirstLineMatch{ .prefix = "--", .content = "lua" }, .first_line_matches = .{ .prefix = "--", .content = "lua" },
.language_server = .{"lua-lsp"}, .language_server = .{"lua-lsp"},
}; };
@ -266,7 +263,7 @@ pub const mail = .{
.extensions = .{ "eml", "mbox" }, .extensions = .{ "eml", "mbox" },
.comment = ">", .comment = ">",
.highlights = "tree-sitter-mail/queries/mail/highlights.scm", .highlights = "tree-sitter-mail/queries/mail/highlights.scm",
.first_line_matches = FirstLineMatch{ .prefix = "From" }, .first_line_matches = .{ .prefix = "From" },
}; };
pub const make = .{ pub const make = .{
@ -410,7 +407,7 @@ pub const python = .{
.icon = "󰌠", .icon = "󰌠",
.extensions = .{ "py", "pyi" }, .extensions = .{ "py", "pyi" },
.comment = "#", .comment = "#",
.first_line_matches = FirstLineMatch{ .prefix = "#!", .content = "python" }, .first_line_matches = .{ .prefix = "#!", .content = "python" },
.language_server = .{"pylsp"}, .language_server = .{"pylsp"},
}; };
@ -523,7 +520,7 @@ pub const xml = .{
.extensions = .{"xml"}, .extensions = .{"xml"},
.comment = "<!--", .comment = "<!--",
.highlights = "tree-sitter-xml/queries/xml/highlights.scm", .highlights = "tree-sitter-xml/queries/xml/highlights.scm",
.first_line_matches = FirstLineMatch{ .prefix = "<?xml " }, .first_line_matches = .{ .prefix = "<?xml " },
.formatter = .{ "xmllint", "--format", "-" }, .formatter = .{ "xmllint", "--format", "-" },
}; };

View file

@ -10,7 +10,6 @@ const Self = @This();
pub const Edit = treez.InputEdit; pub const Edit = treez.InputEdit;
pub const FileType = @import("file_type.zig"); pub const FileType = @import("file_type.zig");
pub const QueryCache = @import("QueryCache.zig");
pub const Range = treez.Range; pub const Range = treez.Range;
pub const Point = treez.Point; pub const Point = treez.Point;
const Input = treez.Input; const Input = treez.Input;
@ -24,40 +23,37 @@ lang: *const Language,
file_type: *const FileType, file_type: *const FileType,
parser: *Parser, parser: *Parser,
query: *Query, query: *Query,
injections: ?*Query, injections: *Query,
tree: ?*treez.Tree = null, tree: ?*treez.Tree = null,
pub fn create(file_type: *const FileType, allocator: std.mem.Allocator, query_cache: *QueryCache) !*Self { pub fn create(file_type: *const FileType, allocator: std.mem.Allocator) !*Self {
const query = try query_cache.get(file_type, .highlights);
const injections = try query_cache.get(file_type, .injections);
const self = try allocator.create(Self); const self = try allocator.create(Self);
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
.lang = file_type.lang_fn() orelse std.debug.panic("tree-sitter parser function failed for language: {s}", .{file_type.name}), .lang = file_type.lang_fn() orelse std.debug.panic("tree-sitter parser function failed for language: {s}", .{file_type.name}),
.file_type = file_type, .file_type = file_type,
.parser = try Parser.create(), .parser = try Parser.create(),
.query = query, .query = try Query.create(self.lang, file_type.highlights),
.injections = injections, .injections = try Query.create(self.lang, file_type.highlights),
}; };
errdefer self.destroy(query_cache); errdefer self.destroy();
try self.parser.setLanguage(self.lang); try self.parser.setLanguage(self.lang);
return self; return self;
} }
pub fn create_file_type(allocator: std.mem.Allocator, lang_name: []const u8, query_cache: *QueryCache) !*Self { pub fn create_file_type(allocator: std.mem.Allocator, lang_name: []const u8) !*Self {
const file_type = FileType.get_by_name(lang_name) orelse return error.NotFound; const file_type = FileType.get_by_name(lang_name) orelse return error.NotFound;
return create(file_type, allocator, query_cache); return create(file_type, allocator);
} }
pub fn create_guess_file_type(allocator: std.mem.Allocator, content: []const u8, file_path: ?[]const u8, query_cache: *QueryCache) !*Self { pub fn create_guess_file_type(allocator: std.mem.Allocator, content: []const u8, file_path: ?[]const u8) !*Self {
const file_type = FileType.guess(file_path, content) orelse return error.NotFound; const file_type = FileType.guess(file_path, content) orelse return error.NotFound;
return create(file_type, allocator, query_cache); return create(file_type, allocator);
} }
pub fn destroy(self: *Self, query_cache: *QueryCache) void { pub fn destroy(self: *Self) void {
if (self.tree) |tree| tree.destroy(); if (self.tree) |tree| tree.destroy();
query_cache.release(self.query, .highlights); self.query.destroy();
if (self.injections) |injections| query_cache.release(injections, .injections);
self.parser.destroy(); self.parser.destroy();
self.allocator.destroy(self); self.allocator.destroy(self);
} }

View file

@ -1,124 +0,0 @@
const std = @import("std");
const cbor = @import("cbor");
const treez = @import("treez");
pub const tss = @import("ts_serializer.zig");
pub fn main() anyerror!void {
const allocator = std.heap.c_allocator;
const args = try std.process.argsAlloc(allocator);
var opt_output_file_path: ?[]const u8 = null;
var i: usize = 1;
while (i < args.len) : (i += 1) {
const arg = args[i];
if (opt_output_file_path != null) fatal("duplicated {s} argument", .{arg});
opt_output_file_path = args[i];
}
const output_file_path = opt_output_file_path orelse fatal("missing output file", .{});
var output_file = std.fs.cwd().createFile(output_file_path, .{}) catch |err| {
fatal("unable to open '{s}': {s}", .{ output_file_path, @errorName(err) });
};
defer output_file.close();
var output = std.ArrayList(u8).init(allocator);
defer output.deinit();
const writer = output.writer();
try cbor.writeMapHeader(writer, file_types.len);
for (file_types) |file_type| {
const lang = file_type.lang_fn() orelse std.debug.panic("tree-sitter parser function failed for language: {s}", .{file_type.name});
try cbor.writeValue(writer, file_type.name);
try cbor.writeMapHeader(writer, if (file_type.injections) |_| 2 else 1);
const highlights_in = try treez.Query.create(lang, file_type.highlights);
const ts_highlights_in: *tss.TSQuery = @alignCast(@ptrCast(highlights_in));
const highlights_cb = try tss.toCbor(ts_highlights_in, allocator);
defer allocator.free(highlights_cb);
try cbor.writeValue(writer, "highlights");
try cbor.writeValue(writer, highlights_cb);
// std.log.info("file_type {s} highlights {d} bytes", .{ file_type.name, highlights_cb.len });
if (file_type.injections) |injections| {
const injections_in = try treez.Query.create(lang, injections);
const ts_injections_in: *tss.TSQuery = @alignCast(@ptrCast(injections_in));
const injections_cb = try tss.toCbor(ts_injections_in, allocator);
defer allocator.free(injections_cb);
try cbor.writeValue(writer, "injections");
try cbor.writeValue(writer, injections_cb);
// std.log.info("file_type {s} injections {d} bytes", .{ file_type.name, injections_cb.len });
}
}
try output_file.writeAll(output.items);
// std.log.info("file_types total {d} bytes", .{output.items.len});
}
fn fatal(comptime format: []const u8, args: anytype) noreturn {
std.debug.print(format, args);
std.process.exit(1);
}
pub const file_types = load_file_types(@import("file_types.zig"));
const FileType = struct {
name: []const u8,
lang_fn: LangFn,
highlights: [:0]const u8,
injections: ?[:0]const u8,
};
const LangFn = *const fn () callconv(.C) ?*const treez.Language;
fn load_file_types(comptime Namespace: type) []const FileType {
comptime switch (@typeInfo(Namespace)) {
.@"struct" => |info| {
var count = 0;
for (info.decls) |_| count += 1;
var construct_types: [count]FileType = undefined;
var i = 0;
for (info.decls) |decl| {
const lang = decl.name;
const args = @field(Namespace, lang);
construct_types[i] = .{
.name = lang,
.lang_fn = if (@hasField(@TypeOf(args), "parser")) args.parser else get_parser(lang),
.highlights = if (@hasField(@TypeOf(args), "highlights"))
@embedFile(args.highlights)
else if (@hasField(@TypeOf(args), "highlights_list"))
@embedFile(args.highlights_list[0]) ++ "\n" ++ @embedFile(args.highlights_list[1])
else
@embedFile("tree-sitter-" ++ lang ++ "/queries/highlights.scm"),
.injections = if (@hasField(@TypeOf(args), "injections"))
@embedFile(args.injections)
else
null,
};
i += 1;
}
const types = construct_types;
return &types;
},
else => @compileError("expected tuple or struct type"),
};
}
fn get_parser(comptime lang: []const u8) LangFn {
const language_name = ft_func_name(lang);
return @extern(?LangFn, .{ .name = "tree_sitter_" ++ language_name }) orelse @compileError(std.fmt.comptimePrint("Cannot find extern tree_sitter_{s}", .{language_name}));
}
fn ft_func_name(comptime lang: []const u8) []const u8 {
var transform: [lang.len]u8 = undefined;
for (lang, 0..) |c, i|
transform[i] = if (c == '-') '_' else c;
const func_name = transform;
return &func_name;
}

View file

@ -1,295 +0,0 @@
/// This file *MUST* be kept in sync with tree-sitter/lib/src/query.c
/// It exactly represents the C structures in memory and must produce
/// the exact same results as the C tree-sitter library version used.
///
/// Yes,... it is not a public API! Here be dragons!
///
const std = @import("std");
const cbor = @import("cbor");
const build_options = @import("build_options");
const treez = if (build_options.use_tree_sitter) @import("treez") else @import("treez_dummy.zig");
pub const Slice = extern struct {
offset: u32,
length: u32,
pub fn cborEncode(self: *const @This(), writer: anytype) !void {
return cbor.writeArray(writer, self.*);
}
pub fn cborExtract(self: *@This(), iter: *[]const u8) cbor.Error!bool {
return cbor.matchValue(iter, .{
cbor.extract(&self.offset),
cbor.extract(&self.length),
});
}
};
pub fn Array(T: type) type {
return extern struct {
contents: ?*T,
size: u32,
capacity: u32,
pub fn cborEncode(self: *const @This(), writer: anytype) !void {
if (self.contents) |contents| {
const arr: []T = @as([*]T, @ptrCast(contents))[0..self.size];
try cbor.writeValue(writer, arr);
return;
}
try cbor.writeValue(writer, null);
}
pub fn cborExtract(self: *@This(), iter: *[]const u8, allocator: std.mem.Allocator) cbor.Error!bool {
var iter_ = iter.*;
if (cbor.matchValue(&iter_, cbor.null_) catch false) {
iter.* = iter_;
self.contents = null;
self.size = 0;
self.capacity = 0;
return true;
}
if (T == u8) {
var arr: []const u8 = undefined;
if (try cbor.matchValue(iter, cbor.extract(&arr))) {
self.contents = @constCast(@ptrCast(arr.ptr));
self.size = @intCast(arr.len);
self.capacity = @intCast(arr.len);
return true;
}
return false;
}
var i: usize = 0;
var n = try cbor.decodeArrayHeader(iter);
var arr: []T = try allocator.alloc(T, n);
while (n > 0) : (n -= 1) {
if (comptime cbor.isExtractableAlloc(T)) {
if (!(cbor.matchValue(iter, cbor.extractAlloc(&arr[i], allocator)) catch return false))
return false;
} else {
if (!(cbor.matchValue(iter, cbor.extract(&arr[i])) catch return false))
return false;
}
i += 1;
}
self.contents = @constCast(@ptrCast(arr.ptr));
self.size = @intCast(arr.len);
self.capacity = @intCast(arr.len);
return true;
}
};
}
pub const SymbolTable = extern struct {
characters: Array(u8),
slices: Array(Slice),
pub fn cborEncode(self: *const @This(), writer: anytype) !void {
return cbor.writeArray(writer, self.*);
}
pub fn cborExtract(self: *@This(), iter: *[]const u8, allocator: std.mem.Allocator) cbor.Error!bool {
return cbor.matchValue(iter, .{
cbor.extractAlloc(&self.characters, allocator),
cbor.extractAlloc(&self.slices, allocator),
});
}
};
pub const CaptureQuantifiers = Array(u8);
pub const PatternEntry = extern struct {
step_index: u16,
pattern_index: u16,
is_rooted: bool,
pub fn cborEncode(self: *const @This(), writer: anytype) !void {
return cbor.writeArray(writer, self.*);
}
pub fn cborExtract(self: *@This(), iter: *[]const u8) cbor.Error!bool {
return cbor.matchValue(iter, .{
cbor.extract(&self.step_index),
cbor.extract(&self.pattern_index),
cbor.extract(&self.is_rooted),
});
}
};
pub const QueryPattern = extern struct {
steps: Slice,
predicate_steps: Slice,
start_byte: u32,
end_byte: u32,
is_non_local: bool,
pub fn cborEncode(self: *const @This(), writer: anytype) !void {
return cbor.writeArray(writer, self.*);
}
pub fn cborExtract(self: *@This(), iter: *[]const u8, allocator: std.mem.Allocator) cbor.Error!bool {
return cbor.matchValue(iter, .{
cbor.extractAlloc(&self.steps, allocator),
cbor.extractAlloc(&self.predicate_steps, allocator),
cbor.extract(&self.start_byte),
cbor.extract(&self.end_byte),
cbor.extract(&self.is_non_local),
});
}
};
pub const StepOffset = extern struct {
byte_offset: u32,
step_index: u16,
pub fn cborEncode(self: *const @This(), writer: anytype) !void {
return cbor.writeArray(writer, self.*);
}
pub fn cborExtract(self: *@This(), iter: *[]const u8) cbor.Error!bool {
return cbor.matchValue(iter, .{
cbor.extract(&self.byte_offset),
cbor.extract(&self.step_index),
});
}
};
pub const MAX_STEP_CAPTURE_COUNT = 3;
pub const TSSymbol = u16;
pub const TSFieldId = u16;
pub const QueryStep = extern struct {
symbol: TSSymbol,
supertype_symbol: TSSymbol,
field: TSFieldId,
capture_ids: [MAX_STEP_CAPTURE_COUNT]u16,
depth: u16,
alternative_index: u16,
negated_field_list_id: u16,
// is_named: u1,
// is_immediate: u1,
// is_last_child: u1,
// is_pass_through: u1,
// is_dead_end: u1,
// alternative_is_immediate: u1,
// contains_captures: u1,
// root_pattern_guaranteed: u1,
flags8: u8,
// parent_pattern_guaranteed: u1,
flags16: u8,
pub fn cborEncode(self: *const @This(), writer: anytype) !void {
return cbor.writeArray(writer, self.*);
}
pub fn cborExtract(self: *@This(), iter: *[]const u8) cbor.Error!bool {
return cbor.matchValue(iter, .{
cbor.extract(&self.symbol),
cbor.extract(&self.supertype_symbol),
cbor.extract(&self.field),
cbor.extract(&self.capture_ids),
cbor.extract(&self.depth),
cbor.extract(&self.alternative_index),
cbor.extract(&self.negated_field_list_id),
cbor.extract(&self.flags8),
cbor.extract(&self.flags16),
});
}
};
pub const PredicateStep = extern struct {
pub const Type = enum(c_uint) {
done,
capture,
string,
};
type: Type,
value_id: u32,
pub fn cborEncode(self: *const @This(), writer: anytype) !void {
return cbor.writeArray(writer, self.*);
}
pub fn cborExtract(self: *@This(), iter: *[]const u8) cbor.Error!bool {
return cbor.matchValue(iter, .{
cbor.extract(&self.type),
cbor.extract(&self.value_id),
});
}
};
pub const TSQuery = extern struct {
captures: SymbolTable,
predicate_values: SymbolTable,
capture_quantifiers: Array(CaptureQuantifiers),
steps: Array(QueryStep),
pattern_map: Array(PatternEntry),
predicate_steps: Array(PredicateStep),
patterns: Array(QueryPattern),
step_offsets: Array(StepOffset),
negated_fields: Array(TSFieldId),
string_buffer: Array(u8),
repeat_symbols_with_rootless_patterns: Array(TSSymbol),
language: usize,
// language: ?*const treez.Language,
wildcard_root_pattern_count: u16,
pub fn cborEncode(self: *const @This(), writer: anytype) !void {
return cbor.writeArray(writer, self.*);
}
pub fn cborExtract(self: *@This(), iter: *[]const u8, allocator: std.mem.Allocator) cbor.Error!bool {
const result = cbor.matchValue(iter, .{
cbor.extractAlloc(&self.captures, allocator),
cbor.extractAlloc(&self.predicate_values, allocator),
cbor.extractAlloc(&self.capture_quantifiers, allocator),
cbor.extractAlloc(&self.steps, allocator),
cbor.extractAlloc(&self.pattern_map, allocator),
cbor.extractAlloc(&self.predicate_steps, allocator),
cbor.extractAlloc(&self.patterns, allocator),
cbor.extractAlloc(&self.step_offsets, allocator),
cbor.extractAlloc(&self.negated_fields, allocator),
cbor.extractAlloc(&self.string_buffer, allocator),
cbor.extractAlloc(&self.repeat_symbols_with_rootless_patterns, allocator),
cbor.extract(&self.language),
cbor.extract(&self.wildcard_root_pattern_count),
});
self.language = 0;
return result;
}
};
pub const SerializeError = error{OutOfMemory};
pub fn toCbor(query: *TSQuery, allocator: std.mem.Allocator) SerializeError![]const u8 {
var cb: std.ArrayListUnmanaged(u8) = .empty;
defer cb.deinit(allocator);
try cbor.writeValue(cb.writer(allocator), query.*);
return cb.toOwnedSlice(allocator);
}
pub const DeserializeError = error{
OutOfMemory,
IntegerTooLarge,
IntegerTooSmall,
InvalidType,
TooShort,
InvalidFloatType,
InvalidArrayType,
InvalidPIntType,
JsonIncompatibleType,
InvalidQueryCbor,
NotAnObject,
};
pub fn fromCbor(cb: []const u8, allocator: std.mem.Allocator) DeserializeError!struct { *TSQuery, *std.heap.ArenaAllocator } {
var arena = try allocator.create(std.heap.ArenaAllocator);
arena.* = std.heap.ArenaAllocator.init(allocator);
errdefer arena.deinit();
const query = try arena.allocator().create(TSQuery);
query.* = undefined;
var iter: []const u8 = cb;
if (!try cbor.matchValue(&iter, cbor.extractAlloc(query, arena.allocator())))
return error.InvalidQueryCbor;
return .{ query, arena };
}

View file

@ -65,7 +65,7 @@ pub fn to_unowned(pimpl: anytype) Self {
pub fn bind(pimpl: anytype, comptime f: *const fn (ctx: @TypeOf(pimpl), from: tp.pid_ref, m: tp.message) Error!bool) Self { pub fn bind(pimpl: anytype, comptime f: *const fn (ctx: @TypeOf(pimpl), from: tp.pid_ref, m: tp.message) Error!bool) Self {
const impl = @typeInfo(@TypeOf(pimpl)); const impl = @typeInfo(@TypeOf(pimpl));
const child: type = impl.pointer.child; const child: type = impl.Pointer.child;
return .{ return .{
.ptr = pimpl, .ptr = pimpl,
.vtable = comptime &.{ .vtable = comptime &.{

View file

@ -55,7 +55,7 @@ pub const VTable = struct {
pub fn to(pimpl: anytype) Self { pub fn to(pimpl: anytype) Self {
const impl = @typeInfo(@TypeOf(pimpl)); const impl = @typeInfo(@TypeOf(pimpl));
const child: type = impl.pointer.child; const child: type = impl.Pointer.child;
return .{ return .{
.ptr = pimpl, .ptr = pimpl,
.plane = &pimpl.plane, .plane = &pimpl.plane,

View file

@ -23,7 +23,7 @@ plane: Plane,
parent: Plane, parent: Plane,
allocator: Allocator, allocator: Allocator,
widgets: ArrayList(WidgetState), widgets: ArrayList(WidgetState),
layout_: Layout, layout: Layout,
direction: Direction, direction: Direction,
box: ?Widget.Box = null, box: ?Widget.Box = null,
ctx: ?*anyopaque = null, ctx: ?*anyopaque = null,
@ -31,7 +31,7 @@ on_render: *const fn (ctx: ?*anyopaque, theme: *const Widget.Theme) void = on_re
after_render: *const fn (ctx: ?*anyopaque, theme: *const Widget.Theme) void = on_render_default, after_render: *const fn (ctx: ?*anyopaque, theme: *const Widget.Theme) void = on_render_default,
on_resize: *const fn (ctx: ?*anyopaque, self: *Self, pos_: Widget.Box) void = on_resize_default, on_resize: *const fn (ctx: ?*anyopaque, self: *Self, pos_: Widget.Box) void = on_resize_default,
pub fn createH(allocator: Allocator, parent: Plane, name: [:0]const u8, layout_: Layout) error{OutOfMemory}!*Self { pub fn createH(allocator: Allocator, parent: Plane, name: [:0]const u8, layout_: Layout) !*Self {
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = try init(allocator, parent, name, .horizontal, layout_, Box{}); self.* = try init(allocator, parent, name, .horizontal, layout_, Box{});
self.plane.hide(); self.plane.hide();
@ -58,7 +58,7 @@ fn init(allocator: Allocator, parent: Plane, name: [:0]const u8, dir: Direction,
.parent = parent, .parent = parent,
.allocator = allocator, .allocator = allocator,
.widgets = ArrayList(WidgetState).init(allocator), .widgets = ArrayList(WidgetState).init(allocator),
.layout_ = layout_, .layout = layout_,
.direction = dir, .direction = dir,
}; };
} }
@ -68,7 +68,7 @@ pub fn widget(self: *Self) Widget {
} }
pub fn layout(self: *Self) Widget.Layout { pub fn layout(self: *Self) Widget.Layout {
return self.layout_; return self.layout;
} }
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void { pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
@ -106,7 +106,7 @@ pub fn remove_all(self: *Self) void {
} }
pub fn pop(self: *Self) ?Widget { pub fn pop(self: *Self) ?Widget {
return if (self.widgets.pop()) |ws| ws.widget else null; return if (self.widgets.popOrNull()) |ws| ws.widget else null;
} }
pub fn empty(self: *const Self) bool { pub fn empty(self: *const Self) bool {

File diff suppressed because it is too large Load diff

View file

@ -8,7 +8,6 @@ const root = @import("root");
const Plane = @import("renderer").Plane; const Plane = @import("renderer").Plane;
const style = @import("renderer").style; const style = @import("renderer").style;
const styles = @import("renderer").styles;
const input = @import("input"); const input = @import("input");
const command = @import("command"); const command = @import("command");
const EventHandler = @import("EventHandler"); const EventHandler = @import("EventHandler");
@ -34,7 +33,7 @@ highlight: bool,
symbols: bool, symbols: bool,
width: usize = 4, width: usize = 4,
editor: *ed.Editor, editor: *ed.Editor,
diff_: diff.AsyncDiffer, diff: diff.AsyncDiffer,
diff_symbols: std.ArrayList(Symbol), diff_symbols: std.ArrayList(Symbol),
const Self = @This(); const Self = @This();
@ -53,7 +52,7 @@ pub fn create(allocator: Allocator, parent: Widget, event_source: Widget, editor
.highlight = tui.config().highlight_current_line_gutter, .highlight = tui.config().highlight_current_line_gutter,
.symbols = tui.config().gutter_symbols, .symbols = tui.config().gutter_symbols,
.editor = editor, .editor = editor,
.diff_ = try diff.create(), .diff = try diff.create(),
.diff_symbols = std.ArrayList(Symbol).init(allocator), .diff_symbols = std.ArrayList(Symbol).init(allocator),
}; };
try tui.message_filters().add(MessageFilter.bind(self, filter_receive)); try tui.message_filters().add(MessageFilter.bind(self, filter_receive));
@ -183,10 +182,10 @@ pub fn render_linear(self: *Self, theme: *const Widget.Theme) void {
if (linenum > self.lines) return; if (linenum > self.lines) return;
if (linenum == self.line + 1) { if (linenum == self.line + 1) {
self.plane.set_style(.{ .fg = theme.editor_gutter_active.fg }); self.plane.set_style(.{ .fg = theme.editor_gutter_active.fg });
self.plane.on_styles(styles.bold); self.plane.on_styles(style.bold);
} else { } else {
self.plane.set_style(.{ .fg = theme.editor_gutter.fg }); self.plane.set_style(.{ .fg = theme.editor_gutter.fg });
self.plane.off_styles(styles.bold); self.plane.off_styles(style.bold);
} }
try self.plane.cursor_move_yx(@intCast(pos), 0); try self.plane.cursor_move_yx(@intCast(pos), 0);
try self.print_digits(linenum, self.render_style); try self.print_digits(linenum, self.render_style);
@ -341,7 +340,7 @@ fn diff_update(self: *Self) !void {
const new = editor.get_current_root() orelse return; const new = editor.get_current_root() orelse return;
const old = if (editor.buffer) |buffer| buffer.last_save orelse return else return; const old = if (editor.buffer) |buffer| buffer.last_save orelse return else return;
const eol_mode = if (editor.buffer) |buffer| buffer.file_eol_mode else return; const eol_mode = if (editor.buffer) |buffer| buffer.file_eol_mode else return;
return self.diff_.diff(diff_result, new, old, eol_mode); return self.diff.diff(diff_result, new, old, eol_mode);
} }
fn diff_result(from: tp.pid_ref, edits: []diff.Diff) void { fn diff_result(from: tp.pid_ref, edits: []diff.Diff) void {
@ -439,7 +438,7 @@ fn int_width(n_: usize) usize {
fn print_digits(self: *Self, n_: anytype, style_: DigitStyle) !void { fn print_digits(self: *Self, n_: anytype, style_: DigitStyle) !void {
var n = n_; var n = n_;
var buf: [12][]const u8 = undefined; var buf: [12][]const u8 = undefined;
var digits: std.ArrayListUnmanaged([]const u8) = .initBuffer(&buf); var digits = std.ArrayListUnmanaged([]const u8).initBuffer(&buf);
while (true) { while (true) {
digits.addOneAssumeCapacity().* = get_digit(n % 10, style_); digits.addOneAssumeCapacity().* = get_digit(n % 10, style_);
n /= 10; n /= 10;

View file

@ -263,34 +263,33 @@ fn select_next(self: *Self, dir: enum { up, down }) void {
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn goto_prev_file(self: *Self, _: Ctx) Result { pub fn goto_prev_file(self: *Self, _: Ctx) Result {
self.select_next(.up); self.select_next(.up);
self.menu.activate_selected(); self.menu.activate_selected();
} }
pub const goto_prev_file_meta: Meta = .{ .description = "Navigate to previous file in the file list" }; pub const goto_prev_file_meta = .{ .description = "Navigate to previous file in the file list" };
pub fn goto_next_file(self: *Self, _: Ctx) Result { pub fn goto_next_file(self: *Self, _: Ctx) Result {
self.select_next(.down); self.select_next(.down);
self.menu.activate_selected(); self.menu.activate_selected();
} }
pub const goto_next_file_meta: Meta = .{ .description = "Navigate to next file in the file list" }; pub const goto_next_file_meta = .{ .description = "Navigate to next file in the file list" };
pub fn select_prev_file(self: *Self, _: Ctx) Result { pub fn select_prev_file(self: *Self, _: Ctx) Result {
self.select_next(.up); self.select_next(.up);
} }
pub const select_prev_file_meta: Meta = .{ .description = "Select previous file in the file list" }; pub const select_prev_file_meta = .{ .description = "Select previous file in the file list" };
pub fn select_next_file(self: *Self, _: Ctx) Result { pub fn select_next_file(self: *Self, _: Ctx) Result {
self.select_next(.down); self.select_next(.down);
} }
pub const select_next_file_meta: Meta = .{ .description = "Select next file in the file list" }; pub const select_next_file_meta = .{ .description = "Select next file in the file list" };
pub fn goto_selected_file(self: *Self, _: Ctx) Result { pub fn goto_selected_file(self: *Self, _: Ctx) Result {
if (self.menu.selected == null) return tp.exit_error(error.NoSelectedFile, @errorReturnTrace()); if (self.menu.selected == null) return tp.exit_error(error.NoSelectedFile, @errorReturnTrace());
self.menu.activate_selected(); self.menu.activate_selected();
} }
pub const goto_selected_file_meta: Meta = .{}; pub const goto_selected_file_meta = .{};
}; };

View file

@ -242,12 +242,6 @@ pub fn get_digit(n: anytype, style_: DigitStyle) []const u8 {
}; };
} }
pub fn get_digit_ascii(char: []const u8, style_: DigitStyle) []const u8 {
if (char.len == 0) return " ";
if (char[0] > '9' or char[0] < '0') return char;
return get_digit(char[0] - '0', style_);
}
const digits_ascii: [10][]const u8 = .{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; const digits_ascii: [10][]const u8 = .{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" };
const digits_digtl: [10][]const u8 = .{ "🯰", "🯱", "🯲", "🯳", "🯴", "🯵", "🯶", "🯷", "🯸", "🯹" }; const digits_digtl: [10][]const u8 = .{ "🯰", "🯱", "🯲", "🯳", "🯴", "🯵", "🯶", "🯷", "🯸", "🯹" };
const digits_subsc: [10][]const u8 = .{ "", "", "", "", "", "", "", "", "", "" }; const digits_subsc: [10][]const u8 = .{ "", "", "", "", "", "", "", "", "", "" };

View file

@ -3,7 +3,6 @@ const build_options = @import("build_options");
const tp = @import("thespian"); const tp = @import("thespian");
const log = @import("log"); const log = @import("log");
const cbor = @import("cbor"); const cbor = @import("cbor");
const builtin = @import("builtin");
const Plane = @import("renderer").Plane; const Plane = @import("renderer").Plane;
const root = @import("root"); const root = @import("root");
@ -301,24 +300,8 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
const x = @min(self.plane.dim_x() -| 32, 8); const x = @min(self.plane.dim_x() -| 32, 8);
self.position_menu(self.v_center(5, self.menu_len, 5), self.center(x, self.menu_w)); self.position_menu(self.v_center(5, self.menu_len, 5), self.center(x, self.menu_w));
} }
self.plane.cursor_move_yx(
@intCast(self.plane.dim_y() - 2),
@intCast(@max(self.plane.dim_x(), root.version.len + 3) - root.version.len - 3),
) catch {};
self.plane.set_style_bg_transparent(style_subtext);
_ = self.plane.print("{s}", .{root.version}) catch return false;
if (builtin.mode == .Debug) {
const debug_warning_text = "debug build";
self.plane.cursor_move_yx(
@intCast(self.plane.dim_y() - 3),
@intCast(@max(self.plane.dim_x(), debug_warning_text.len + 3) - debug_warning_text.len - 3),
) catch {};
self.plane.set_style_bg_transparent(theme.editor_error);
_ = self.plane.print("{s}", .{debug_warning_text}) catch return false;
}
const more = self.menu.render(theme); const more = self.menu.render(theme);
return more or self.fire != null; return more or self.fire != null;
} }
@ -359,29 +342,28 @@ const Commands = command.Collection(cmds);
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn save_all(_: *Self, _: Ctx) Result { pub fn save_all(_: *Self, _: Ctx) Result {
if (tui.get_buffer_manager()) |bm| if (tui.get_buffer_manager()) |bm|
bm.save_all() catch |e| return tp.exit_error(e, @errorReturnTrace()); bm.save_all() catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const save_all_meta: Meta = .{ .description = "Save all changed files" }; pub const save_all_meta = .{ .description = "Save all changed files" };
pub fn home_menu_down(self: *Self, _: Ctx) Result { pub fn home_menu_down(self: *Self, _: Ctx) Result {
self.menu.select_down(); self.menu.select_down();
} }
pub const home_menu_down_meta: Meta = .{}; pub const home_menu_down_meta = .{};
pub fn home_menu_up(self: *Self, _: Ctx) Result { pub fn home_menu_up(self: *Self, _: Ctx) Result {
self.menu.select_up(); self.menu.select_up();
} }
pub const home_menu_up_meta: Meta = .{}; pub const home_menu_up_meta = .{};
pub fn home_menu_activate(self: *Self, _: Ctx) Result { pub fn home_menu_activate(self: *Self, _: Ctx) Result {
self.menu.activate_selected(); self.menu.activate_selected();
} }
pub const home_menu_activate_meta: Meta = .{}; pub const home_menu_activate_meta = .{};
pub fn home_sheeran(self: *Self, _: Ctx) Result { pub fn home_sheeran(self: *Self, _: Ctx) Result {
self.fire = if (self.fire) |*fire| ret: { self.fire = if (self.fire) |*fire| ret: {
@ -389,7 +371,7 @@ const cmds = struct {
break :ret null; break :ret null;
} else try Fire.init(self.allocator, self.plane); } else try Fire.init(self.allocator, self.plane);
} }
pub const home_sheeran_meta: Meta = .{}; pub const home_sheeran_meta = .{};
}; };
const Fire = @import("Fire.zig"); const Fire = @import("Fire.zig");

View file

@ -7,7 +7,6 @@ const syntax = @import("syntax");
const Plane = @import("renderer").Plane; const Plane = @import("renderer").Plane;
const style = @import("renderer").style; const style = @import("renderer").style;
const styles = @import("renderer").styles;
const EventHandler = @import("EventHandler"); const EventHandler = @import("EventHandler");
const tui = @import("tui.zig"); const tui = @import("tui.zig");
@ -169,31 +168,31 @@ fn show_color(self: *Self, tag: []const u8, c_: ?Widget.Theme.Color) void {
fn show_font(self: *Self, font: ?Widget.Theme.FontStyle) void { fn show_font(self: *Self, font: ?Widget.Theme.FontStyle) void {
if (font) |fs| switch (fs) { if (font) |fs| switch (fs) {
.normal => { .normal => {
self.plane.set_styles(styles.normal); self.plane.set_styles(style.normal);
_ = self.plane.print(" normal", .{}) catch return; _ = self.plane.print(" normal", .{}) catch return;
}, },
.bold => { .bold => {
self.plane.set_styles(styles.bold); self.plane.set_styles(style.bold);
_ = self.plane.print(" bold", .{}) catch return; _ = self.plane.print(" bold", .{}) catch return;
}, },
.italic => { .italic => {
self.plane.set_styles(styles.italic); self.plane.set_styles(style.italic);
_ = self.plane.print(" italic", .{}) catch return; _ = self.plane.print(" italic", .{}) catch return;
}, },
.underline => { .underline => {
self.plane.set_styles(styles.underline); self.plane.set_styles(style.underline);
_ = self.plane.print(" underline", .{}) catch return; _ = self.plane.print(" underline", .{}) catch return;
}, },
.undercurl => { .undercurl => {
self.plane.set_styles(styles.undercurl); self.plane.set_styles(style.undercurl);
_ = self.plane.print(" undercurl", .{}) catch return; _ = self.plane.print(" undercurl", .{}) catch return;
}, },
.strikethrough => { .strikethrough => {
self.plane.set_styles(styles.struck); self.plane.set_styles(style.struck);
_ = self.plane.print(" strikethrough", .{}) catch return; _ = self.plane.print(" strikethrough", .{}) catch return;
}, },
}; };
self.plane.set_styles(styles.normal); self.plane.set_styles(style.normal);
} }
fn reset_style(self: *Self) void { fn reset_style(self: *Self) void {

View file

@ -48,7 +48,7 @@ views_widget: Widget,
active_view: ?usize = 0, active_view: ?usize = 0,
panels: ?*WidgetList = null, panels: ?*WidgetList = null,
last_match_text: ?[]const u8 = null, last_match_text: ?[]const u8 = null,
location_history_: location_history, location_history: location_history,
buffer_manager: Buffer.Manager, buffer_manager: Buffer.Manager,
find_in_files_state: enum { init, adding, done } = .done, find_in_files_state: enum { init, adding, done } = .done,
file_list_type: FileListType = .find_in_files, file_list_type: FileListType = .find_in_files,
@ -60,9 +60,7 @@ const FileListType = enum {
find_in_files, find_in_files,
}; };
pub const CreateError = error{ OutOfMemory, ThespianSpawnFailed }; pub fn create(allocator: std.mem.Allocator) !Widget {
pub fn create(allocator: std.mem.Allocator) CreateError!Widget {
const self = try allocator.create(Self); const self = try allocator.create(Self);
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
@ -70,7 +68,7 @@ pub fn create(allocator: std.mem.Allocator) CreateError!Widget {
.widgets = undefined, .widgets = undefined,
.widgets_widget = undefined, .widgets_widget = undefined,
.floating_views = WidgetStack.init(allocator), .floating_views = WidgetStack.init(allocator),
.location_history_ = try location_history.create(), .location_history = try location_history.create(),
.views = undefined, .views = undefined,
.views_widget = undefined, .views_widget = undefined,
.buffer_manager = Buffer.Manager.init(allocator), .buffer_manager = Buffer.Manager.init(allocator),
@ -117,11 +115,6 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool {
var end_line: usize = undefined; var end_line: usize = undefined;
var end_pos: usize = undefined; var end_pos: usize = undefined;
var lines: []const u8 = undefined; var lines: []const u8 = undefined;
var same_file: bool = undefined;
var goto_args: []const u8 = undefined;
var line: i64 = undefined;
var column: i64 = undefined;
if (try m.match(.{ "REF", tp.extract(&path), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos), tp.extract(&lines) })) { if (try m.match(.{ "REF", tp.extract(&path), tp.extract(&begin_line), tp.extract(&begin_pos), tp.extract(&end_line), tp.extract(&end_pos), tp.extract(&lines) })) {
try self.add_find_in_files_result(.references, path, begin_line, begin_pos, end_line, end_pos, lines, .Information); try self.add_find_in_files_result(.references, path, begin_line, begin_pos, end_line, end_pos, lines, .Information);
return true; return true;
@ -146,12 +139,6 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool {
.end = .{ .row = end_line, .col = end_pos }, .end = .{ .row = end_line, .col = end_pos },
}); });
return true; return true;
} else if (try m.match(.{ "navigate_complete", tp.extract(&same_file), tp.extract(&path), tp.extract(&goto_args), tp.extract(&line), tp.extract(&column) })) {
cmds.navigate_complete(self, same_file, path, goto_args, line, column) catch |e| return tp.exit_error(e, @errorReturnTrace());
return true;
} else if (try m.match(.{ "navigate_complete", tp.extract(&same_file), tp.extract(&path), tp.extract(&goto_args), tp.null_, tp.null_ })) {
cmds.navigate_complete(self, same_file, path, goto_args, null, null) catch |e| return tp.exit_error(e, @errorReturnTrace());
return true;
} }
return if (try self.floating_views.send(from_, m)) true else self.widgets.send(from_, m); return if (try self.floating_views.send(from_, m)) true else self.widgets.send(from_, m);
} }
@ -193,7 +180,7 @@ fn bottom_bar_primary_drag(self: *Self, y: usize) tp.result {
}; };
const h = self.plane.dim_y(); const h = self.plane.dim_y();
self.panel_height = @max(1, h - @min(h, y + 1)); self.panel_height = @max(1, h - @min(h, y + 1));
panels.layout_ = .{ .static = self.panel_height.? }; panels.layout = .{ .static = self.panel_height.? };
if (self.panel_height == 1) { if (self.panel_height == 1) {
self.panel_height = null; self.panel_height = null;
command.executeName("toggle_panel", .{}) catch {}; command.executeName("toggle_panel", .{}) catch {};
@ -273,19 +260,19 @@ const cmds = struct {
try self.check_all_not_dirty(); try self.check_all_not_dirty();
try tp.self_pid().send("quit"); try tp.self_pid().send("quit");
} }
pub const quit_meta: Meta = .{ .description = "Quit" }; pub const quit_meta = .{ .description = "Quit" };
pub fn quit_without_saving(_: *Self, _: Ctx) Result { pub fn quit_without_saving(_: *Self, _: Ctx) Result {
try tp.self_pid().send("quit"); try tp.self_pid().send("quit");
} }
pub const quit_without_saving_meta: Meta = .{ .description = "Quit without saving" }; pub const quit_without_saving_meta = .{ .description = "Quit without saving" };
pub fn open_project_cwd(self: *Self, _: Ctx) Result { pub fn open_project_cwd(self: *Self, _: Ctx) Result {
try project_manager.open("."); try project_manager.open(".");
if (self.top_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" }); if (self.top_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" });
if (self.bottom_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" }); if (self.bottom_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" });
} }
pub const open_project_cwd_meta: Meta = .{}; pub const open_project_cwd_meta = .{};
pub fn open_project_dir(self: *Self, ctx: Ctx) Result { pub fn open_project_dir(self: *Self, ctx: Ctx) Result {
var project_dir: []const u8 = undefined; var project_dir: []const u8 = undefined;
@ -297,7 +284,7 @@ const cmds = struct {
if (self.top_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" }); if (self.top_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" });
if (self.bottom_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" }); if (self.bottom_bar) |bar| _ = try bar.msg(.{ "PRJ", "open" });
} }
pub const open_project_dir_meta: Meta = .{ .arguments = &.{.string} }; pub const open_project_dir_meta = .{ .arguments = &.{.string} };
pub fn close_project(_: *Self, ctx: Ctx) Result { pub fn close_project(_: *Self, ctx: Ctx) Result {
var project_dir: []const u8 = undefined; var project_dir: []const u8 = undefined;
@ -337,7 +324,7 @@ const cmds = struct {
if (try project_manager.request_most_recent_file(self.allocator)) |file_path| if (try project_manager.request_most_recent_file(self.allocator)) |file_path|
self.show_file_async_and_free(file_path); self.show_file_async_and_free(file_path);
} }
pub const change_project_meta: Meta = .{ .arguments = &.{.string} }; pub const change_project_meta = .{ .arguments = &.{.string} };
pub fn navigate(self: *Self, ctx: Ctx) Result { pub fn navigate(self: *Self, ctx: Ctx) Result {
tui.reset_drag_context(); tui.reset_drag_context();
@ -384,42 +371,12 @@ const cmds = struct {
const same_file = if (self.get_active_file_path()) |fp| std.mem.eql(u8, fp, f) else false; const same_file = if (self.get_active_file_path()) |fp| std.mem.eql(u8, fp, f) else false;
const have_editor_metadata = if (self.buffer_manager.get_buffer_for_file(f)) |_| true else false; const have_editor_metadata = if (self.buffer_manager.get_buffer_for_file(f)) |_| true else false;
if (!same_file and !have_editor_metadata and line == null) { if (!same_file and !have_editor_metadata and line == null)
const ctx_: struct { if (try project_manager.get_mru_position(self.allocator, f)) |pos| {
allocator: std.mem.Allocator, line = @intCast(pos.row);
from: tp.pid, column = @intCast(pos.col);
same_file: bool,
path: []const u8,
goto_args: []const u8,
pub fn deinit(ctx_: *@This()) void {
ctx_.from.deinit();
ctx_.allocator.free(ctx_.path);
ctx_.allocator.free(ctx_.goto_args);
}
pub fn receive(ctx_: @This(), rsp: tp.message) !void {
var line_: ?i64 = null;
var column_: ?i64 = null;
_ = try cbor.match(rsp.buf, .{ tp.extract(&line_), tp.extract(&column_) });
try ctx_.from.send(.{ "navigate_complete", ctx_.same_file, ctx_.path, ctx_.goto_args, line_, column_ });
}
} = .{
.allocator = self.allocator,
.from = tp.self_pid().clone(),
.same_file = same_file,
.path = try self.allocator.dupe(u8, f),
.goto_args = try self.allocator.dupe(u8, goto_args),
}; };
try project_manager.get_mru_position(self.allocator, f, ctx_);
return;
}
return cmds.navigate_complete(self, same_file, f, goto_args, line, column);
}
pub const navigate_meta: Meta = .{ .arguments = &.{.object} };
fn navigate_complete(self: *Self, same_file: bool, f: []const u8, goto_args: []const u8, line: ?i64, column: ?i64) Result {
if (!same_file) { if (!same_file) {
if (self.get_active_editor()) |editor| { if (self.get_active_editor()) |editor| {
editor.send_editor_jump_source() catch {}; editor.send_editor_jump_source() catch {};
@ -438,6 +395,7 @@ const cmds = struct {
} }
tui.need_render(); tui.need_render();
} }
pub const navigate_meta = .{ .arguments = &.{.object} };
pub fn open_help(self: *Self, _: Ctx) Result { pub fn open_help(self: *Self, _: Ctx) Result {
tui.reset_drag_context(); tui.reset_drag_context();
@ -445,7 +403,7 @@ const cmds = struct {
try command.executeName("open_scratch_buffer", command.fmt(.{ "help", @embedFile("help.md"), "markdown" })); try command.executeName("open_scratch_buffer", command.fmt(.{ "help", @embedFile("help.md"), "markdown" }));
tui.need_render(); tui.need_render();
} }
pub const open_help_meta: Meta = .{ .description = "Open help" }; pub const open_help_meta = .{ .description = "Open help" };
pub fn open_font_test_text(self: *Self, _: Ctx) Result { pub fn open_font_test_text(self: *Self, _: Ctx) Result {
tui.reset_drag_context(); tui.reset_drag_context();
@ -453,7 +411,7 @@ const cmds = struct {
try command.executeName("open_scratch_buffer", command.fmt(.{ "font test", @import("fonts.zig").font_test_text, "text" })); try command.executeName("open_scratch_buffer", command.fmt(.{ "font test", @import("fonts.zig").font_test_text, "text" }));
tui.need_render(); tui.need_render();
} }
pub const open_font_test_text_meta: Meta = .{ .description = "Open font glyph test text" }; pub const open_font_test_text_meta = .{ .description = "Open font glyph test text" };
pub fn open_version_info(self: *Self, _: Ctx) Result { pub fn open_version_info(self: *Self, _: Ctx) Result {
tui.reset_drag_context(); tui.reset_drag_context();
@ -461,19 +419,19 @@ const cmds = struct {
try command.executeName("open_scratch_buffer", command.fmt(.{ "version", root.version_info, "diff" })); try command.executeName("open_scratch_buffer", command.fmt(.{ "version", root.version_info, "diff" }));
tui.need_render(); tui.need_render();
} }
pub const open_version_info_meta: Meta = .{ .description = "Version" }; pub const open_version_info_meta = .{ .description = "Version" };
pub fn open_config(_: *Self, _: Ctx) Result { pub fn open_config(_: *Self, _: Ctx) Result {
const file_name = try root.get_config_file_name(@import("config")); const file_name = try root.get_config_file_name(@import("config"));
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name[0 .. file_name.len - 5] } }); try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name[0 .. file_name.len - 5] } });
} }
pub const open_config_meta: Meta = .{ .description = "Edit configuration" }; pub const open_config_meta = .{ .description = "Edit configuration" };
pub fn open_gui_config(_: *Self, _: Ctx) Result { pub fn open_gui_config(_: *Self, _: Ctx) Result {
const file_name = try root.get_config_file_name(@import("gui_config")); const file_name = try root.get_config_file_name(@import("gui_config"));
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name[0 .. file_name.len - ".json".len] } }); try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name[0 .. file_name.len - ".json".len] } });
} }
pub const open_gui_config_meta: Meta = .{ .description = "Edit gui configuration" }; pub const open_gui_config_meta = .{ .description = "Edit gui configuration" };
pub fn open_tabs_style_config(self: *Self, _: Ctx) Result { pub fn open_tabs_style_config(self: *Self, _: Ctx) Result {
try self.open_style_config(@import("status/tabs.zig").Style); try self.open_style_config(@import("status/tabs.zig").Style);
@ -493,7 +451,7 @@ const cmds = struct {
try command.executeName("open_scratch_buffer", .{ .args = args }); try command.executeName("open_scratch_buffer", .{ .args = args });
tui.need_render(); tui.need_render();
} }
pub const create_scratch_buffer_meta: Meta = .{ .arguments = &.{ .string, .string, .string } }; pub const create_scratch_buffer_meta = .{ .arguments = &.{ .string, .string, .string } };
pub fn create_new_file(self: *Self, _: Ctx) Result { pub fn create_new_file(self: *Self, _: Ctx) Result {
var n: usize = 1; var n: usize = 1;
@ -530,7 +488,7 @@ const cmds = struct {
logger.print("deleted buffer {s}", .{file_path}); logger.print("deleted buffer {s}", .{file_path});
tui.need_render(); tui.need_render();
} }
pub const delete_buffer_meta: Meta = .{ .arguments = &.{.string} }; pub const delete_buffer_meta = .{ .arguments = &.{.string} };
pub fn close_buffer(self: *Self, ctx: Ctx) Result { pub fn close_buffer(self: *Self, ctx: Ctx) Result {
var file_path: []const u8 = undefined; var file_path: []const u8 = undefined;
@ -546,7 +504,7 @@ const cmds = struct {
_ = self.buffer_manager.close_buffer(buffer); _ = self.buffer_manager.close_buffer(buffer);
tui.need_render(); tui.need_render();
} }
pub const close_buffer_meta: Meta = .{ .arguments = &.{.string} }; pub const close_buffer_meta = .{ .arguments = &.{.string} };
pub fn restore_session(self: *Self, _: Ctx) Result { pub fn restore_session(self: *Self, _: Ctx) Result {
if (tp.env.get().str("project").len == 0) { if (tp.env.get().str("project").len == 0) {
@ -556,7 +514,7 @@ const cmds = struct {
try self.read_restore_info(); try self.read_restore_info();
tui.need_render(); tui.need_render();
} }
pub const restore_session_meta: Meta = .{}; pub const restore_session_meta = .{};
pub fn toggle_panel(self: *Self, _: Ctx) Result { pub fn toggle_panel(self: *Self, _: Ctx) Result {
if (self.is_panel_view_showing(logview)) if (self.is_panel_view_showing(logview))
@ -570,58 +528,52 @@ const cmds = struct {
else else
try self.toggle_panel_view(logview, false); try self.toggle_panel_view(logview, false);
} }
pub const toggle_panel_meta: Meta = .{ .description = "Toggle panel" }; pub const toggle_panel_meta = .{ .description = "Toggle panel" };
pub fn toggle_logview(self: *Self, _: Ctx) Result { pub fn toggle_logview(self: *Self, _: Ctx) Result {
try self.toggle_panel_view(logview, false); try self.toggle_panel_view(logview, false);
} }
pub const toggle_logview_meta: Meta = .{}; pub const toggle_logview_meta = .{};
pub fn show_logview(self: *Self, _: Ctx) Result { pub fn show_logview(self: *Self, _: Ctx) Result {
try self.toggle_panel_view(logview, true); try self.toggle_panel_view(logview, true);
} }
pub const show_logview_meta: Meta = .{ .description = "View log" }; pub const show_logview_meta = .{ .description = "View log" };
pub fn toggle_inputview(self: *Self, _: Ctx) Result { pub fn toggle_inputview(self: *Self, _: Ctx) Result {
try self.toggle_panel_view(input_view, false); try self.toggle_panel_view(input_view, false);
} }
pub const toggle_inputview_meta: Meta = .{ .description = "Toggle raw input log" }; pub const toggle_inputview_meta = .{ .description = "Toggle raw input log" };
pub fn toggle_inspector_view(self: *Self, _: Ctx) Result { pub fn toggle_inspector_view(self: *Self, _: Ctx) Result {
try self.toggle_panel_view(@import("inspector_view.zig"), false); try self.toggle_panel_view(@import("inspector_view.zig"), false);
} }
pub const toggle_inspector_view_meta: Meta = .{ .description = "Toggle inspector view" }; pub const toggle_inspector_view_meta = .{ .description = "Toggle inspector view" };
pub fn show_inspector_view(self: *Self, _: Ctx) Result { pub fn show_inspector_view(self: *Self, _: Ctx) Result {
try self.toggle_panel_view(@import("inspector_view.zig"), true); try self.toggle_panel_view(@import("inspector_view.zig"), true);
} }
pub const show_inspector_view_meta: Meta = .{}; pub const show_inspector_view_meta = .{};
pub fn close_find_in_files_results(self: *Self, _: Ctx) Result {
if (self.file_list_type == .find_in_files and self.is_panel_view_showing(filelist_view))
try self.toggle_panel_view(filelist_view, false);
}
pub const close_find_in_files_results_meta: Meta = .{ .description = "Close find in files results view" };
pub fn jump_back(self: *Self, _: Ctx) Result { pub fn jump_back(self: *Self, _: Ctx) Result {
try self.location_history_.back(location_jump); try self.location_history.back(location_jump);
} }
pub const jump_back_meta: Meta = .{ .description = "Navigate back to previous history location" }; pub const jump_back_meta = .{ .description = "Navigate back to previous history location" };
pub fn jump_forward(self: *Self, _: Ctx) Result { pub fn jump_forward(self: *Self, _: Ctx) Result {
try self.location_history_.forward(location_jump); try self.location_history.forward(location_jump);
} }
pub const jump_forward_meta: Meta = .{ .description = "Navigate forward to next history location" }; pub const jump_forward_meta = .{ .description = "Navigate forward to next history location" };
pub fn show_home(self: *Self, _: Ctx) Result { pub fn show_home(self: *Self, _: Ctx) Result {
return self.create_home(); return self.create_home();
} }
pub const show_home_meta: Meta = .{}; pub const show_home_meta = .{};
pub fn add_split(self: *Self, _: Ctx) Result { pub fn add_split(self: *Self, _: Ctx) Result {
return self.create_home_split(); return self.create_home_split();
} }
pub const add_split_meta: Meta = .{}; pub const add_split_meta = .{};
pub fn gutter_mode_next(self: *Self, _: Ctx) Result { pub fn gutter_mode_next(self: *Self, _: Ctx) Result {
const config = tui.config_mut(); const config = tui.config_mut();
@ -638,7 +590,7 @@ const cmds = struct {
gutter.mode = mode; gutter.mode = mode;
} }
} }
pub const gutter_mode_next_meta: Meta = .{ .description = "Next gutter mode" }; pub const gutter_mode_next_meta = .{ .description = "Next gutter mode" };
pub fn gutter_style_next(self: *Self, _: Ctx) Result { pub fn gutter_style_next(self: *Self, _: Ctx) Result {
const config = tui.config_mut(); const config = tui.config_mut();
@ -673,7 +625,7 @@ const cmds = struct {
try command.executeName("goto_next_diagnostic", ctx); try command.executeName("goto_next_diagnostic", ctx);
} }
} }
pub const goto_next_file_or_diagnostic_meta: Meta = .{ .description = "Navigate to next file or diagnostic location" }; pub const goto_next_file_or_diagnostic_meta = .{ .description = "Navigate to next file or diagnostic location" };
pub fn goto_prev_file_or_diagnostic(self: *Self, ctx: Ctx) Result { pub fn goto_prev_file_or_diagnostic(self: *Self, ctx: Ctx) Result {
if (self.is_panel_view_showing(filelist_view)) { if (self.is_panel_view_showing(filelist_view)) {
@ -685,7 +637,7 @@ const cmds = struct {
try command.executeName("goto_prev_diagnostic", ctx); try command.executeName("goto_prev_diagnostic", ctx);
} }
} }
pub const goto_prev_file_or_diagnostic_meta: Meta = .{ .description = "Navigate to previous file or diagnostic location" }; pub const goto_prev_file_or_diagnostic_meta = .{ .description = "Navigate to previous file or diagnostic location" };
pub fn add_diagnostic(self: *Self, ctx: Ctx) Result { pub fn add_diagnostic(self: *Self, ctx: Ctx) Result {
var file_path: []const u8 = undefined; var file_path: []const u8 = undefined;
@ -720,7 +672,7 @@ const cmds = struct {
ed.Diagnostic.to_severity(severity), ed.Diagnostic.to_severity(severity),
); );
} }
pub const add_diagnostic_meta: Meta = .{ .arguments = &.{ .string, .string, .string, .string, .integer, .integer, .integer, .integer, .integer } }; pub const add_diagnostic_meta = .{ .arguments = &.{ .string, .string, .string, .string, .integer, .integer, .integer, .integer, .integer } };
pub fn rename_symbol_item(self: *Self, ctx: Ctx) Result { pub fn rename_symbol_item(self: *Self, ctx: Ctx) Result {
const editor = self.get_active_editor() orelse return; const editor = self.get_active_editor() orelse return;
@ -764,8 +716,8 @@ const cmds = struct {
} }
} }
} }
pub const rename_symbol_item_meta: Meta = .{ .arguments = &.{.array} }; pub const rename_symbol_item_meta = .{ .arguments = &.{.array} };
pub const rename_symbol_item_elem_meta: Meta = .{ .arguments = &.{ .string, .integer, .integer, .integer, .integer, .string } }; pub const rename_symbol_item_elem_meta = .{ .arguments = &.{ .string, .integer, .integer, .integer, .integer, .string } };
pub fn clear_diagnostics(self: *Self, ctx: Ctx) Result { pub fn clear_diagnostics(self: *Self, ctx: Ctx) Result {
var file_path: []const u8 = undefined; var file_path: []const u8 = undefined;
@ -778,7 +730,7 @@ const cmds = struct {
if (self.file_list_type == .diagnostics and self.is_panel_view_showing(filelist_view)) if (self.file_list_type == .diagnostics and self.is_panel_view_showing(filelist_view))
try self.toggle_panel_view(filelist_view, false); try self.toggle_panel_view(filelist_view, false);
} }
pub const clear_diagnostics_meta: Meta = .{ .arguments = &.{.string} }; pub const clear_diagnostics_meta = .{ .arguments = &.{.string} };
pub fn show_diagnostics(self: *Self, _: Ctx) Result { pub fn show_diagnostics(self: *Self, _: Ctx) Result {
const editor = self.get_active_editor() orelse return; const editor = self.get_active_editor() orelse return;
@ -796,12 +748,12 @@ const cmds = struct {
); );
} }
} }
pub const show_diagnostics_meta: Meta = .{ .description = "Show diagnostics panel" }; pub const show_diagnostics_meta = .{ .description = "Show diagnostics panel" };
pub fn open_previous_file(self: *Self, _: Ctx) Result { pub fn open_previous_file(self: *Self, _: Ctx) Result {
self.show_file_async(self.get_next_mru_buffer() orelse return error.Stop); self.show_file_async(self.get_next_mru_buffer() orelse return error.Stop);
} }
pub const open_previous_file_meta: Meta = .{ .description = "Open the previous file" }; pub const open_previous_file_meta = .{ .description = "Open the previous file" };
pub fn system_paste(self: *Self, _: Ctx) Result { pub fn system_paste(self: *Self, _: Ctx) Result {
if (builtin.os.tag == .windows) { if (builtin.os.tag == .windows) {
@ -811,7 +763,7 @@ const cmds = struct {
} }
tui.rdr().request_system_clipboard(); tui.rdr().request_system_clipboard();
} }
pub const system_paste_meta: Meta = .{ .description = "Paste from system clipboard" }; pub const system_paste_meta = .{ .description = "Paste from system clipboard" };
pub fn find_in_files_query(self: *Self, ctx: Ctx) Result { pub fn find_in_files_query(self: *Self, ctx: Ctx) Result {
var query: []const u8 = undefined; var query: []const u8 = undefined;
@ -825,7 +777,7 @@ const cmds = struct {
defer rg.deinit(); defer rg.deinit();
self.find_in_files_state = .init; self.find_in_files_state = .init;
} }
pub const find_in_files_query_meta: Meta = .{ .arguments = &.{.string} }; pub const find_in_files_query_meta = .{ .arguments = &.{.string} };
pub fn shell_execute_log(self: *Self, ctx: Ctx) Result { pub fn shell_execute_log(self: *Self, ctx: Ctx) Result {
if (!try ctx.args.match(.{ tp.string, tp.more })) if (!try ctx.args.match(.{ tp.string, tp.more }))
@ -837,7 +789,7 @@ const cmds = struct {
.exit = shell.log_exit_err_handler, .exit = shell.log_exit_err_handler,
}); });
} }
pub const shell_execute_log_meta: Meta = .{ .arguments = &.{.string} }; pub const shell_execute_log_meta = .{ .arguments = &.{.string} };
pub fn shell_execute_insert(self: *Self, ctx: Ctx) Result { pub fn shell_execute_insert(self: *Self, ctx: Ctx) Result {
if (!try ctx.args.match(.{ tp.string, tp.more })) if (!try ctx.args.match(.{ tp.string, tp.more }))
@ -863,7 +815,7 @@ const cmds = struct {
}; };
try shell.execute(self.allocator, cmd, .{ .out = handlers.out }); try shell.execute(self.allocator, cmd, .{ .out = handlers.out });
} }
pub const shell_execute_insert_meta: Meta = .{ .arguments = &.{.string} }; pub const shell_execute_insert_meta = .{ .arguments = &.{.string} };
pub fn shell_execute_stream(self: *Self, ctx: Ctx) Result { pub fn shell_execute_stream(self: *Self, ctx: Ctx) Result {
if (!try ctx.args.match(.{ tp.string, tp.more })) if (!try ctx.args.match(.{ tp.string, tp.more }))
@ -891,7 +843,7 @@ const cmds = struct {
const buffer_ref = self.buffer_manager.buffer_to_ref(buffer); const buffer_ref = self.buffer_manager.buffer_to_ref(buffer);
try shell.execute(self.allocator, cmd, .{ .context = buffer_ref, .out = handlers.out, .err = handlers.out, .exit = handlers.exit }); try shell.execute(self.allocator, cmd, .{ .context = buffer_ref, .out = handlers.out, .err = handlers.out, .exit = handlers.exit });
} }
pub const shell_execute_stream_meta: Meta = .{ .arguments = &.{.string} }; pub const shell_execute_stream_meta = .{ .arguments = &.{.string} };
pub fn shell_execute_stream_output(self: *Self, ctx: Ctx) Result { pub fn shell_execute_stream_output(self: *Self, ctx: Ctx) Result {
var buffer_ref: usize = 0; var buffer_ref: usize = 0;
@ -913,7 +865,7 @@ const cmds = struct {
buffer.update(root_); buffer.update(root_);
tui.need_render(); tui.need_render();
} }
pub const shell_execute_stream_output_meta: Meta = .{ .arguments = &.{ .integer, .string } }; pub const shell_execute_stream_output_meta = .{ .arguments = &.{ .integer, .string } };
pub fn shell_execute_stream_output_complete(self: *Self, ctx: Ctx) Result { pub fn shell_execute_stream_output_complete(self: *Self, ctx: Ctx) Result {
var buffer_ref: usize = 0; var buffer_ref: usize = 0;
@ -927,7 +879,7 @@ const cmds = struct {
buffer.mark_clean(); buffer.mark_clean();
tui.need_render(); tui.need_render();
} }
pub const shell_execute_stream_output_complete_meta: Meta = .{ .arguments = &.{ .integer, .string } }; pub const shell_execute_stream_output_complete_meta = .{ .arguments = &.{ .integer, .string } };
pub fn adjust_fontsize(_: *Self, ctx: Ctx) Result { pub fn adjust_fontsize(_: *Self, ctx: Ctx) Result {
var amount: f32 = undefined; var amount: f32 = undefined;
@ -936,7 +888,7 @@ const cmds = struct {
if (build_options.gui) if (build_options.gui)
tui.rdr().adjust_fontsize(amount); tui.rdr().adjust_fontsize(amount);
} }
pub const adjust_fontsize_meta: Meta = .{ .arguments = &.{.float} }; pub const adjust_fontsize_meta = .{ .arguments = &.{.float} };
pub fn set_fontsize(_: *Self, ctx: Ctx) Result { pub fn set_fontsize(_: *Self, ctx: Ctx) Result {
var fontsize: f32 = undefined; var fontsize: f32 = undefined;
@ -945,13 +897,13 @@ const cmds = struct {
if (build_options.gui) if (build_options.gui)
tui.rdr().set_fontsize(fontsize); tui.rdr().set_fontsize(fontsize);
} }
pub const set_fontsize_meta: Meta = .{ .arguments = &.{.float} }; pub const set_fontsize_meta = .{ .arguments = &.{.float} };
pub fn reset_fontsize(_: *Self, _: Ctx) Result { pub fn reset_fontsize(_: *Self, _: Ctx) Result {
if (build_options.gui) if (build_options.gui)
tui.rdr().reset_fontsize(); tui.rdr().reset_fontsize();
} }
pub const reset_fontsize_meta: Meta = .{ .description = "Reset font to configured size" }; pub const reset_fontsize_meta = .{ .description = "Reset font to configured size" };
pub fn set_fontface(_: *Self, ctx: Ctx) Result { pub fn set_fontface(_: *Self, ctx: Ctx) Result {
var fontface: []const u8 = undefined; var fontface: []const u8 = undefined;
@ -960,23 +912,23 @@ const cmds = struct {
if (build_options.gui) if (build_options.gui)
tui.rdr().set_fontface(fontface); tui.rdr().set_fontface(fontface);
} }
pub const set_fontface_meta: Meta = .{ .arguments = &.{.float} }; pub const set_fontface_meta = .{ .arguments = &.{.float} };
pub fn reset_fontface(_: *Self, _: Ctx) Result { pub fn reset_fontface(_: *Self, _: Ctx) Result {
if (build_options.gui) if (build_options.gui)
tui.rdr().reset_fontface(); tui.rdr().reset_fontface();
} }
pub const reset_fontface_meta: Meta = .{ .description = "Reset font to configured face" }; pub const reset_fontface_meta = .{ .description = "Reset font to configured face" };
pub fn next_tab(self: *Self, _: Ctx) Result { pub fn next_tab(self: *Self, _: Ctx) Result {
_ = try self.widgets_widget.msg(.{"next_tab"}); _ = try self.widgets_widget.msg(.{"next_tab"});
} }
pub const next_tab_meta: Meta = .{ .description = "Switch to next tab" }; pub const next_tab_meta = .{ .description = "Switch to next tab" };
pub fn previous_tab(self: *Self, _: Ctx) Result { pub fn previous_tab(self: *Self, _: Ctx) Result {
_ = try self.widgets_widget.msg(.{"previous_tab"}); _ = try self.widgets_widget.msg(.{"previous_tab"});
} }
pub const previous_tab_meta: Meta = .{ .description = "Switch to previous tab" }; pub const previous_tab_meta = .{ .description = "Switch to previous tab" };
}; };
pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result { pub fn handle_editor_event(self: *Self, _: tp.pid_ref, m: tp.message) tp.result {
@ -1022,13 +974,13 @@ pub fn location_update(self: *Self, m: tp.message) tp.result {
if (try m.match(.{ tp.any, tp.any, tp.any, tp.extract(&row), tp.extract(&col) })) { if (try m.match(.{ tp.any, tp.any, tp.any, tp.extract(&row), tp.extract(&col) })) {
if (row == 0 and col == 0) return; if (row == 0 and col == 0) return;
project_manager.update_mru(file_path, row, col, ephemeral) catch {}; project_manager.update_mru(file_path, row, col, ephemeral) catch {};
return self.location_history_.update(file_path, .{ .row = row + 1, .col = col + 1 }, null); return self.location_history.update(file_path, .{ .row = row + 1, .col = col + 1 }, null);
} }
var sel: location_history.Selection = .{}; var sel: location_history.Selection = .{};
if (try m.match(.{ tp.any, tp.any, tp.any, tp.extract(&row), tp.extract(&col), tp.extract(&sel.begin.row), tp.extract(&sel.begin.col), tp.extract(&sel.end.row), tp.extract(&sel.end.col) })) { if (try m.match(.{ tp.any, tp.any, tp.any, tp.extract(&row), tp.extract(&col), tp.extract(&sel.begin.row), tp.extract(&sel.begin.col), tp.extract(&sel.end.row), tp.extract(&sel.end.col) })) {
project_manager.update_mru(file_path, row, col, ephemeral) catch {}; project_manager.update_mru(file_path, row, col, ephemeral) catch {};
return self.location_history_.update(file_path, .{ .row = row + 1, .col = col + 1 }, sel); return self.location_history.update(file_path, .{ .row = row + 1, .col = col + 1 }, sel);
} }
} }

View file

@ -1,16 +1,10 @@
const std = @import("std"); const std = @import("std");
const log = @import("log"); const log = @import("log");
const tp = @import("thespian");
const location_history = @import("location_history"); const location_history = @import("location_history");
const command = @import("command"); const command = @import("command");
const cmd = command.executeName; const cmd = command.executeName;
const tui = @import("../tui.zig"); const tui = @import("../tui.zig");
const Editor = @import("../editor.zig").Editor;
const CurSel = @import("../editor.zig").CurSel;
const Buffer = @import("Buffer");
const Cursor = Buffer.Cursor;
const Selection = Buffer.Selection;
var commands: Commands = undefined; var commands: Commands = undefined;
@ -27,39 +21,38 @@ const Commands = command.Collection(cmds_);
const cmds_ = struct { const cmds_ = struct {
pub const Target = void; pub const Target = void;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn w(_: *void, _: Ctx) Result { pub fn w(_: *void, _: Ctx) Result {
try cmd("save_file", .{}); try cmd("save_file", .{});
} }
pub const w_meta: Meta = .{ .description = "w (write/save file)" }; pub const w_meta = .{ .description = "w (write/save file)" };
pub fn q(_: *void, _: Ctx) Result { pub fn q(_: *void, _: Ctx) Result {
try cmd("quit", .{}); try cmd("quit", .{});
} }
pub const q_meta: Meta = .{ .description = "q (quit)" }; pub const q_meta = .{ .description = "q (quit)" };
pub fn @"q!"(_: *void, _: Ctx) Result { pub fn @"q!"(_: *void, _: Ctx) Result {
try cmd("quit_without_saving", .{}); try cmd("quit_without_saving", .{});
} }
pub const @"q!_meta": Meta = .{ .description = "q! (quit without saving)" }; pub const @"q!_meta" = .{ .description = "q! (quit without saving)" };
pub fn wq(_: *void, _: Ctx) Result { pub fn wq(_: *void, _: Ctx) Result {
try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } })); try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } }));
} }
pub const wq_meta: Meta = .{ .description = "wq (write/save file and quit)" }; pub const wq_meta = .{ .description = "wq (write/save file and quit)" };
pub fn o(_: *void, _: Ctx) Result { pub fn o(_: *void, _: Ctx) Result {
try cmd("open_file", .{}); try cmd("open_file", .{});
} }
pub const o_meta: Meta = .{ .description = "o (open file)" }; pub const o_meta = .{ .description = "o (open file)" };
pub fn @"wq!"(_: *void, _: Ctx) Result { pub fn @"wq!"(_: *void, _: Ctx) Result {
cmd("save_file", .{}) catch {}; cmd("save_file", .{}) catch {};
try cmd("quit_without_saving", .{}); try cmd("quit_without_saving", .{});
} }
pub const @"wq!_meta": Meta = .{ .description = "wq! (write/save file and quit without saving)" }; pub const @"wq!_meta" = .{ .description = "wq! (write/save file and quit without saving)" };
pub fn save_selection(_: *void, _: Ctx) Result { pub fn save_selection(_: *void, _: Ctx) Result {
const logger = log.logger("helix-mode"); const logger = log.logger("helix-mode");
@ -72,279 +65,10 @@ const cmds_ = struct {
.begin = .{ .row = sel.begin.row, .col = sel.begin.col }, .begin = .{ .row = sel.begin.row, .col = sel.begin.col },
.end = .{ .row = sel.end.row, .col = sel.end.col }, .end = .{ .row = sel.end.row, .col = sel.end.col },
} else null; } else null;
mv.location_history_.update(file_path, .{ mv.location_history.update(file_path, .{
.row = primary.cursor.row + 1, .row = primary.cursor.row + 1,
.col = primary.cursor.col + 1, .col = primary.cursor.col + 1,
}, sel); }, sel);
} }
pub const save_selection_meta: Meta = .{ .description = "Save current selection to location history" }; pub const save_selection_meta = .{ .description = "Save current selection to location history" };
pub fn extend_line_below(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
const sel = cursel.enable_selection_normal();
sel.normalize();
try Editor.move_cursor_begin(root, &sel.begin, ed.metrics);
try Editor.move_cursor_end(root, &sel.end, ed.metrics);
try Editor.move_cursor_right(root, &sel.end, ed.metrics);
cursel.cursor = sel.end;
};
}
ed.clamp();
}
pub const extend_line_below_meta: Meta = .{ .arguments = &.{.integer}, .description = "Select current line, if already selected, extend to next line" };
pub fn move_next_word_start(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
cursel.selection = null;
};
ed.with_selections_const_repeat(root, Editor.move_cursor_word_right_vim, ctx) catch {};
ed.clamp();
}
pub const move_next_word_start_meta: Meta = .{ .description = "Move next word start", .arguments = &.{.integer} };
pub fn move_prev_word_start(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
cursel.selection = null;
};
ed.with_selections_const_repeat(root, move_cursor_word_left_helix, ctx) catch {};
ed.clamp();
}
pub const move_prev_word_start_meta: Meta = .{ .description = "Move previous word start", .arguments = &.{.integer} };
pub fn move_next_word_end(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
cursel.selection = null;
};
ed.with_selections_const_repeat(root, move_cursor_word_right_end_helix, ctx) catch {};
ed.clamp();
}
pub const move_next_word_end_meta: Meta = .{ .description = "Move next word end", .arguments = &.{.integer} };
pub fn cut_forward_internal_inclusive(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const b = try ed.buf_for_update();
const text, const root = try ed.cut_to(move_noop, b.root);
ed.set_clipboard_internal(text);
try ed.update_buf(root);
ed.clamp();
}
pub const cut_forward_internal_inclusive_meta: Meta = .{ .description = "Cut next character to internal clipboard (inclusive)" };
pub fn select_right_helix(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
const sel = try cursel.enable_selection(root, ed.metrics);
// handling left to right transition
const sel_begin: i32 = @intCast(sel.begin.col);
const sel_end: i32 = @intCast(sel.end.col);
if ((sel_begin - sel_end) == 1 and sel.begin.row == sel.end.row) {
try Editor.move_cursor_right(root, &sel.end, ed.metrics);
sel.begin.col -= 1;
}
try Editor.move_cursor_right(root, &sel.end, ed.metrics);
cursel.cursor = sel.end;
cursel.check_selection(root, ed.metrics);
};
}
ed.clamp();
}
pub const select_right_helix_meta: Meta = .{ .description = "Select right", .arguments = &.{.integer} };
pub fn select_left_helix(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
if (cursel.selection == null) {
cursel.selection = Selection.from_cursor(&cursel.cursor);
try cursel.selection.?.begin.move_right(root, ed.metrics);
}
if (cursel.selection) |*sel| {
try Editor.move_cursor_left(root, &sel.end, ed.metrics);
cursel.cursor = sel.end;
if (sel.begin.col == sel.end.col and sel.begin.row == sel.end.row) {
try sel.begin.move_right(root, ed.metrics);
try Editor.move_cursor_left(root, &sel.end, ed.metrics);
cursel.cursor = sel.end;
}
}
cursel.check_selection(root, ed.metrics);
};
}
ed.clamp();
}
pub const select_left_helix_meta: Meta = .{ .description = "Select left", .arguments = &.{.integer} };
pub fn select_to_char_right_helix(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
const sel = try cursel.enable_selection(root, ed.metrics);
try Editor.move_cursor_to_char_right(root, &sel.end, ctx, ed.metrics);
try Editor.move_cursor_right(root, &sel.end, ed.metrics);
cursel.cursor = sel.end;
cursel.check_selection(root, ed.metrics);
};
ed.clamp();
}
pub const select_to_char_right_helix_meta: Meta = .{ .description = "Move to char right" };
pub fn copy_helix(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
var first = true;
var text = std.ArrayList(u8).init(ed.allocator);
if (ed.get_primary().selection) |sel| if (sel.begin.col == 0 and sel.end.row > sel.begin.row) try text.appendSlice("\n");
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
if (cursel.selection) |sel| {
const copy_text = try Editor.copy_selection(root, sel, ed.allocator, ed.metrics);
if (first) {
first = false;
} else {
try text.appendSlice("\n");
}
try text.appendSlice(copy_text);
}
};
if (text.items.len > 0) {
if (text.items.len > 100) {
ed.logger.print("copy:{s}...", .{std.fmt.fmtSliceEscapeLower(text.items[0..100])});
} else {
ed.logger.print("copy:{s}", .{std.fmt.fmtSliceEscapeLower(text.items)});
}
ed.set_clipboard_internal(text.items);
}
}
pub const copy_helix_meta: Meta = .{ .description = "Copy selection to clipboard (helix)" };
pub fn paste_after(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
var text: []const u8 = undefined;
if (!(ctx.args.buf.len > 0 and try ctx.args.match(.{tp.extract(&text)}))) {
if (ed.clipboard) |text_| text = text_ else return;
}
ed.logger.print("paste: {d} bytes", .{text.len});
const b = try ed.buf_for_update();
var root = b.root;
if (std.mem.eql(u8, text[text.len - 1 ..], "\n")) text = text[0 .. text.len - 1];
if (std.mem.indexOfScalar(u8, text, '\n') != null and text[0] == '\n') {
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
root = try insert_line(ed, root, cursel, text, b.allocator);
};
} else {
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
root = try insert(ed, root, cursel, text, b.allocator);
};
}
try ed.update_buf(root);
ed.clamp();
ed.need_render();
}
pub const paste_after_meta: Meta = .{ .description = "Paste from clipboard after selection" };
}; };
fn move_cursor_word_left_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void {
try Editor.move_cursor_left(root, cursor, metrics);
// Consume " "
while (Editor.is_whitespace_at_cursor(root, cursor, metrics)) {
try Editor.move_cursor_left(root, cursor, metrics);
}
var next = cursor.*;
next.move_left(root, metrics) catch return;
var next_next = next;
next_next.move_left(root, metrics) catch return;
const cur = next.test_at(root, Editor.is_not_word_char, metrics);
const nxt = next_next.test_at(root, Editor.is_not_word_char, metrics);
if (cur != nxt) {
try Editor.move_cursor_left(root, cursor, metrics);
return;
} else {
try move_cursor_word_left_helix(root, cursor, metrics);
}
}
fn move_noop(_: Buffer.Root, _: *Cursor, _: Buffer.Metrics) error{Stop}!void {}
fn move_cursor_word_right_end_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void {
try Editor.move_cursor_right(root, cursor, metrics);
Editor.move_cursor_right_until(root, cursor, Editor.is_word_boundary_right_vim, metrics);
try cursor.move_right(root, metrics);
}
fn insert(ed: *Editor, root: Buffer.Root, cursel: *CurSel, s: []const u8, allocator: std.mem.Allocator) !Buffer.Root {
var root_ = root;
const cursor = &cursel.cursor;
if (cursel.selection == null) try cursor.move_right(root_, ed.metrics);
const begin = cursel.cursor;
cursor.row, cursor.col, root_ = try root_.insert_chars(cursor.row, cursor.col, s, allocator, ed.metrics);
cursor.target = cursor.col;
ed.nudge_insert(.{ .begin = begin, .end = cursor.* }, cursel, s.len);
cursel.selection = Selection{ .begin = begin, .end = cursor.* };
return root_;
}
fn insert_line(ed: *Editor, root: Buffer.Root, cursel: *CurSel, s: []const u8, allocator: std.mem.Allocator) !Buffer.Root {
var root_ = root;
const cursor = &cursel.cursor;
cursel.disable_selection(root, ed.metrics);
cursel.cursor.move_end(root, ed.metrics);
var begin = cursel.cursor;
try begin.move_right(root, ed.metrics);
cursor.row, cursor.col, root_ = try root_.insert_chars(cursor.row, cursor.col, s, allocator, ed.metrics);
cursor.target = cursor.col;
cursel.selection = Selection{ .begin = begin, .end = cursor.* };
return root_;
}

View file

@ -67,19 +67,18 @@ pub fn Create(options: type) type {
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn mini_mode_reset(self: *Self, _: Ctx) Result { pub fn mini_mode_reset(self: *Self, _: Ctx) Result {
self.input.clearRetainingCapacity(); self.input.clearRetainingCapacity();
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_reset_meta: Meta = .{ .description = "Clear input" }; pub const mini_mode_reset_meta = .{ .description = "Clear input" };
pub fn mini_mode_cancel(_: *Self, _: Ctx) Result { pub fn mini_mode_cancel(_: *Self, _: Ctx) Result {
command.executeName("exit_mini_mode", .{}) catch {}; command.executeName("exit_mini_mode", .{}) catch {};
} }
pub const mini_mode_cancel_meta: Meta = .{ .description = "Cancel input" }; pub const mini_mode_cancel_meta = .{ .description = "Cancel input" };
pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result { pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result {
if (self.input.items.len > 0) { if (self.input.items.len > 0) {
@ -87,7 +86,7 @@ pub fn Create(options: type) type {
} }
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_delete_backwards_meta: Meta = .{ .description = "Delete backwards" }; pub const mini_mode_delete_backwards_meta = .{ .description = "Delete backwards" };
pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result {
var egc: u32 = 0; var egc: u32 = 0;
@ -98,7 +97,7 @@ pub fn Create(options: type) type {
try self.input.appendSlice(buf[0..bytes]); try self.input.appendSlice(buf[0..bytes]);
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; pub const mini_mode_insert_code_point_meta = .{ .arguments = &.{.integer} };
pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result {
var bytes: []const u8 = undefined; var bytes: []const u8 = undefined;
@ -107,18 +106,18 @@ pub fn Create(options: type) type {
try self.input.appendSlice(bytes); try self.input.appendSlice(bytes);
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_insert_bytes_meta = .{ .arguments = &.{.string} };
pub fn mini_mode_select(self: *Self, _: Ctx) Result { pub fn mini_mode_select(self: *Self, _: Ctx) Result {
options.select(self); options.select(self);
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_select_meta: Meta = .{ .description = "Select" }; pub const mini_mode_select_meta = .{ .description = "Select" };
pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result { pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result {
return mini_mode_insert_bytes(self, ctx); return mini_mode_insert_bytes(self, ctx);
} }
pub const mini_mode_paste_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_paste_meta = .{ .arguments = &.{.string} };
}; };
}; };
} }

View file

@ -250,7 +250,6 @@ pub fn Create(options: type) type {
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn mini_mode_reset(self: *Self, _: Ctx) Result { pub fn mini_mode_reset(self: *Self, _: Ctx) Result {
@ -258,18 +257,18 @@ pub fn Create(options: type) type {
self.file_path.clearRetainingCapacity(); self.file_path.clearRetainingCapacity();
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_reset_meta: Meta = .{ .description = "Clear input" }; pub const mini_mode_reset_meta = .{ .description = "Clear input" };
pub fn mini_mode_cancel(_: *Self, _: Ctx) Result { pub fn mini_mode_cancel(_: *Self, _: Ctx) Result {
command.executeName("exit_mini_mode", .{}) catch {}; command.executeName("exit_mini_mode", .{}) catch {};
} }
pub const mini_mode_cancel_meta: Meta = .{ .description = "Cancel input" }; pub const mini_mode_cancel_meta = .{ .description = "Cancel input" };
pub fn mini_mode_delete_to_previous_path_segment(self: *Self, _: Ctx) Result { pub fn mini_mode_delete_to_previous_path_segment(self: *Self, _: Ctx) Result {
self.delete_to_previous_path_segment(); self.delete_to_previous_path_segment();
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_delete_to_previous_path_segment_meta: Meta = .{ .description = "Delete to previous path segment" }; pub const mini_mode_delete_to_previous_path_segment_meta = .{ .description = "Delete to previous path segment" };
pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result { pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result {
if (self.file_path.items.len > 0) { if (self.file_path.items.len > 0) {
@ -278,25 +277,25 @@ pub fn Create(options: type) type {
} }
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_delete_backwards_meta: Meta = .{ .description = "Delete backwards" }; pub const mini_mode_delete_backwards_meta = .{ .description = "Delete backwards" };
pub fn mini_mode_try_complete_file(self: *Self, _: Ctx) Result { pub fn mini_mode_try_complete_file(self: *Self, _: Ctx) Result {
self.try_complete_file() catch |e| return tp.exit_error(e, @errorReturnTrace()); self.try_complete_file() catch |e| return tp.exit_error(e, @errorReturnTrace());
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_try_complete_file_meta: Meta = .{ .description = "Complete file" }; pub const mini_mode_try_complete_file_meta = .{ .description = "Complete file" };
pub fn mini_mode_try_complete_file_forward(self: *Self, ctx: Ctx) Result { pub fn mini_mode_try_complete_file_forward(self: *Self, ctx: Ctx) Result {
self.complete_trigger_count = 0; self.complete_trigger_count = 0;
return mini_mode_try_complete_file(self, ctx); return mini_mode_try_complete_file(self, ctx);
} }
pub const mini_mode_try_complete_file_forward_meta: Meta = .{ .description = "Complete file forward" }; pub const mini_mode_try_complete_file_forward_meta = .{ .description = "Complete file forward" };
pub fn mini_mode_reverse_complete_file(self: *Self, _: Ctx) Result { pub fn mini_mode_reverse_complete_file(self: *Self, _: Ctx) Result {
self.reverse_complete_file() catch |e| return tp.exit_error(e, @errorReturnTrace()); self.reverse_complete_file() catch |e| return tp.exit_error(e, @errorReturnTrace());
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_reverse_complete_file_meta: Meta = .{ .description = "Reverse complete file" }; pub const mini_mode_reverse_complete_file_meta = .{ .description = "Reverse complete file" };
pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result {
var egc: u32 = 0; var egc: u32 = 0;
@ -308,7 +307,7 @@ pub fn Create(options: type) type {
try self.file_path.appendSlice(buf[0..bytes]); try self.file_path.appendSlice(buf[0..bytes]);
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; pub const mini_mode_insert_code_point_meta = .{ .arguments = &.{.integer} };
pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result {
var bytes: []const u8 = undefined; var bytes: []const u8 = undefined;
@ -318,18 +317,18 @@ pub fn Create(options: type) type {
try self.file_path.appendSlice(bytes); try self.file_path.appendSlice(bytes);
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_insert_bytes_meta = .{ .arguments = &.{.string} };
pub fn mini_mode_select(self: *Self, _: Ctx) Result { pub fn mini_mode_select(self: *Self, _: Ctx) Result {
options.select(self); options.select(self);
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_select_meta: Meta = .{ .description = "Select" }; pub const mini_mode_select_meta = .{ .description = "Select" };
pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result { pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result {
return mini_mode_insert_bytes(self, ctx); return mini_mode_insert_bytes(self, ctx);
} }
pub const mini_mode_paste_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_paste_meta = .{ .arguments = &.{.string} };
}; };
}; };
} }

View file

@ -18,7 +18,7 @@ const name = "󱎸 find";
const Commands = command.Collection(cmds); const Commands = command.Collection(cmds);
allocator: Allocator, allocator: Allocator,
input_: ArrayList(u8), input: ArrayList(u8),
last_input: ArrayList(u8), last_input: ArrayList(u8),
start_view: ed.View, start_view: ed.View,
start_cursor: ed.Cursor, start_cursor: ed.Cursor,
@ -31,7 +31,7 @@ pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
.input_ = ArrayList(u8).init(allocator), .input = ArrayList(u8).init(allocator),
.last_input = ArrayList(u8).init(allocator), .last_input = ArrayList(u8).init(allocator),
.start_view = editor.view, .start_view = editor.view,
.start_cursor = editor.get_primary().cursor, .start_cursor = editor.get_primary().cursor,
@ -41,7 +41,7 @@ pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.
if (editor.get_primary().selection) |sel| ret: { if (editor.get_primary().selection) |sel| ret: {
const text = editor.get_selection(sel, self.allocator) catch break :ret; const text = editor.get_selection(sel, self.allocator) catch break :ret;
defer self.allocator.free(text); defer self.allocator.free(text);
try self.input_.appendSlice(text); try self.input.appendSlice(text);
} }
var mode = try keybind.mode("mini/find", allocator, .{ var mode = try keybind.mode("mini/find", allocator, .{
.insert_command = "mini_mode_insert_bytes", .insert_command = "mini_mode_insert_bytes",
@ -52,7 +52,7 @@ pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
self.commands.deinit(); self.commands.deinit();
self.input_.deinit(); self.input.deinit();
self.last_input.deinit(); self.last_input.deinit();
self.allocator.destroy(self); self.allocator.destroy(self);
} }
@ -73,24 +73,24 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
fn insert_code_point(self: *Self, c: u32) !void { fn insert_code_point(self: *Self, c: u32) !void {
var buf: [16]u8 = undefined; var buf: [16]u8 = undefined;
const bytes = input.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e, @errorReturnTrace()); const bytes = input.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e, @errorReturnTrace());
try self.input_.appendSlice(buf[0..bytes]); try self.input.appendSlice(buf[0..bytes]);
} }
fn insert_bytes(self: *Self, bytes: []const u8) !void { fn insert_bytes(self: *Self, bytes: []const u8) !void {
try self.input_.appendSlice(bytes); try self.input.appendSlice(bytes);
} }
fn flush_input(self: *Self) !void { fn flush_input(self: *Self) !void {
if (self.input_.items.len > 0) { if (self.input.items.len > 0) {
if (eql(u8, self.input_.items, self.last_input.items)) if (eql(u8, self.input.items, self.last_input.items))
return; return;
self.last_input.clearRetainingCapacity(); self.last_input.clearRetainingCapacity();
try self.last_input.appendSlice(self.input_.items); try self.last_input.appendSlice(self.input.items);
self.editor.find_operation = .goto_next_match; self.editor.find_operation = .goto_next_match;
const primary = self.editor.get_primary(); const primary = self.editor.get_primary();
primary.selection = null; primary.selection = null;
primary.cursor = self.start_cursor; primary.cursor = self.start_cursor;
try self.editor.find_in_buffer(self.input_.items); try self.editor.find_in_buffer(self.input.items);
} else { } else {
self.editor.get_primary().selection = null; self.editor.get_primary().selection = null;
self.editor.init_matches_update(); self.editor.init_matches_update();
@ -114,9 +114,9 @@ fn find_history_prev(self: *Self) void {
if (pos > 0) self.history_pos = pos - 1; if (pos > 0) self.history_pos = pos - 1;
} else { } else {
self.history_pos = history.items.len - 1; self.history_pos = history.items.len - 1;
if (self.input_.items.len > 0) if (self.input.items.len > 0)
self.editor.push_find_history(self.editor.allocator.dupe(u8, self.input_.items) catch return); self.editor.push_find_history(self.editor.allocator.dupe(u8, self.input.items) catch return);
if (eql(u8, history.items[self.history_pos.?], self.input_.items) and self.history_pos.? > 0) if (eql(u8, history.items[self.history_pos.?], self.input.items) and self.history_pos.? > 0)
self.history_pos = self.history_pos.? - 1; self.history_pos = self.history_pos.? - 1;
} }
self.load_history(self.history_pos.?); self.load_history(self.history_pos.?);
@ -134,40 +134,39 @@ fn find_history_next(self: *Self) void {
fn load_history(self: *Self, pos: usize) void { fn load_history(self: *Self, pos: usize) void {
if (self.editor.find_history) |*history| { if (self.editor.find_history) |*history| {
self.input_.clearRetainingCapacity(); self.input.clearRetainingCapacity();
self.input_.appendSlice(history.items[pos]) catch {}; self.input.appendSlice(history.items[pos]) catch {};
} }
} }
fn update_mini_mode_text(self: *Self) void { fn update_mini_mode_text(self: *Self) void {
if (tui.mini_mode()) |mini_mode| { if (tui.mini_mode()) |mini_mode| {
mini_mode.text = self.input_.items; mini_mode.text = self.input.items;
mini_mode.cursor = tui.egc_chunk_width(self.input_.items, 0, 8); mini_mode.cursor = tui.egc_chunk_width(self.input.items, 0, 8);
} }
} }
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn mini_mode_reset(self: *Self, _: Ctx) Result { pub fn mini_mode_reset(self: *Self, _: Ctx) Result {
self.input_.clearRetainingCapacity(); self.input.clearRetainingCapacity();
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_reset_meta: Meta = .{ .description = "Clear input" }; pub const mini_mode_reset_meta = .{ .description = "Clear input" };
pub fn mini_mode_cancel(self: *Self, _: Ctx) Result { pub fn mini_mode_cancel(self: *Self, _: Ctx) Result {
self.cancel(); self.cancel();
} }
pub const mini_mode_cancel_meta: Meta = .{ .description = "Cancel input" }; pub const mini_mode_cancel_meta = .{ .description = "Cancel input" };
pub fn mini_mode_select(self: *Self, _: Ctx) Result { pub fn mini_mode_select(self: *Self, _: Ctx) Result {
self.editor.push_find_history(self.input_.items); self.editor.push_find_history(self.input.items);
self.cmd("exit_mini_mode", .{}) catch {}; self.cmd("exit_mini_mode", .{}) catch {};
} }
pub const mini_mode_select_meta: Meta = .{ .description = "Select" }; pub const mini_mode_select_meta = .{ .description = "Select" };
pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result {
var egc: u32 = 0; var egc: u32 = 0;
@ -176,7 +175,7 @@ const cmds = struct {
self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace());
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; pub const mini_mode_insert_code_point_meta = .{ .arguments = &.{.integer} };
pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result {
var bytes: []const u8 = undefined; var bytes: []const u8 = undefined;
@ -185,28 +184,28 @@ const cmds = struct {
self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace());
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_insert_bytes_meta = .{ .arguments = &.{.string} };
pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result { pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result {
self.input_.resize(self.input_.items.len - tui.egc_last(self.input_.items).len) catch {}; self.input.resize(self.input.items.len - tui.egc_last(self.input.items).len) catch {};
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_delete_backwards_meta: Meta = .{ .description = "Delete backwards" }; pub const mini_mode_delete_backwards_meta = .{ .description = "Delete backwards" };
pub fn mini_mode_history_prev(self: *Self, _: Ctx) Result { pub fn mini_mode_history_prev(self: *Self, _: Ctx) Result {
self.find_history_prev(); self.find_history_prev();
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_history_prev_meta: Meta = .{ .description = "History previous" }; pub const mini_mode_history_prev_meta = .{ .description = "History previous" };
pub fn mini_mode_history_next(self: *Self, _: Ctx) Result { pub fn mini_mode_history_next(self: *Self, _: Ctx) Result {
self.find_history_next(); self.find_history_next();
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_history_next_meta: Meta = .{ .description = "History next" }; pub const mini_mode_history_next_meta = .{ .description = "History next" };
pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result { pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result {
return mini_mode_insert_bytes(self, ctx); return mini_mode_insert_bytes(self, ctx);
} }
pub const mini_mode_paste_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_paste_meta = .{ .arguments = &.{.string} };
}; };

View file

@ -19,7 +19,7 @@ const max_query_size = 1024;
allocator: Allocator, allocator: Allocator,
buf: [max_query_size]u8 = undefined, buf: [max_query_size]u8 = undefined,
input_: []u8 = "", input: []u8 = "",
last_buf: [max_query_size]u8 = undefined, last_buf: [max_query_size]u8 = undefined,
last_input: []u8 = "", last_input: []u8 = "",
commands: Commands = undefined, commands: Commands = undefined,
@ -31,7 +31,7 @@ pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui.
if (tui.get_active_selection(self.allocator)) |text| { if (tui.get_active_selection(self.allocator)) |text| {
defer self.allocator.free(text); defer self.allocator.free(text);
@memcpy(self.buf[0..text.len], text); @memcpy(self.buf[0..text.len], text);
self.input_ = self.buf[0..text.len]; self.input = self.buf[0..text.len];
} }
var mode = try keybind.mode("mini/find_in_files", allocator, .{ var mode = try keybind.mode("mini/find_in_files", allocator, .{
.insert_command = "mini_mode_insert_bytes", .insert_command = "mini_mode_insert_bytes",
@ -59,57 +59,55 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool {
} }
fn insert_code_point(self: *Self, c: u32) !void { fn insert_code_point(self: *Self, c: u32) !void {
if (self.input_.len + 6 >= self.buf.len) if (self.input.len + 6 >= self.buf.len)
return; return;
const bytes = try input.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input_.len..]); const bytes = try input.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]);
self.input_ = self.buf[0 .. self.input_.len + bytes]; self.input = self.buf[0 .. self.input.len + bytes];
} }
fn insert_bytes(self: *Self, bytes_: []const u8) !void { fn insert_bytes(self: *Self, bytes_: []const u8) !void {
const bytes = bytes_[0..@min(self.buf.len - self.input_.len, bytes_.len)]; const bytes = bytes_[0..@min(self.buf.len - self.input.len, bytes_.len)];
const newlen = self.input_.len + bytes.len; const newlen = self.input.len + bytes.len;
@memcpy(self.buf[self.input_.len..newlen], bytes); @memcpy(self.buf[self.input.len..newlen], bytes);
self.input_ = self.buf[0..newlen]; self.input = self.buf[0..newlen];
} }
fn start_query(self: *Self) !void { fn start_query(self: *Self) !void {
if (self.input_.len < 2 or eql(u8, self.input_, self.last_input)) if (self.input.len < 2 or eql(u8, self.input, self.last_input))
return; return;
@memcpy(self.last_buf[0..self.input_.len], self.input_); @memcpy(self.last_buf[0..self.input.len], self.input);
self.last_input = self.last_buf[0..self.input_.len]; self.last_input = self.last_buf[0..self.input.len];
try command.executeName("find_in_files_query", command.fmt(.{self.input_})); try command.executeName("find_in_files_query", command.fmt(.{self.input}));
} }
fn update_mini_mode_text(self: *Self) void { fn update_mini_mode_text(self: *Self) void {
if (tui.mini_mode()) |mini_mode| { if (tui.mini_mode()) |mini_mode| {
mini_mode.text = self.input_; mini_mode.text = self.input;
mini_mode.cursor = tui.egc_chunk_width(self.input_, 0, 8); mini_mode.cursor = tui.egc_chunk_width(self.input, 0, 8);
} }
} }
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn mini_mode_reset(self: *Self, _: Ctx) Result { pub fn mini_mode_reset(self: *Self, _: Ctx) Result {
self.input_ = ""; self.input = "";
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_reset_meta: Meta = .{ .description = "Clear input" }; pub const mini_mode_reset_meta = .{ .description = "Clear input" };
pub fn mini_mode_cancel(_: *Self, _: Ctx) Result { pub fn mini_mode_cancel(_: *Self, _: Ctx) Result {
command.executeName("close_find_in_files_results", .{}) catch {};
command.executeName("exit_mini_mode", .{}) catch {}; command.executeName("exit_mini_mode", .{}) catch {};
} }
pub const mini_mode_cancel_meta: Meta = .{ .description = "Cancel input" }; pub const mini_mode_cancel_meta = .{ .description = "Cancel input" };
pub fn mini_mode_select(_: *Self, _: Ctx) Result { pub fn mini_mode_select(_: *Self, _: Ctx) Result {
command.executeName("goto_selected_file", .{}) catch {}; command.executeName("goto_selected_file", .{}) catch {};
return command.executeName("exit_mini_mode", .{}); return command.executeName("exit_mini_mode", .{});
} }
pub const mini_mode_select_meta: Meta = .{ .description = "Select" }; pub const mini_mode_select_meta = .{ .description = "Select" };
pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result {
var egc: u32 = 0; var egc: u32 = 0;
@ -118,7 +116,7 @@ const cmds = struct {
self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace());
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; pub const mini_mode_insert_code_point_meta = .{ .arguments = &.{.integer} };
pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result {
var bytes: []const u8 = undefined; var bytes: []const u8 = undefined;
@ -127,16 +125,16 @@ const cmds = struct {
self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace());
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_insert_bytes_meta = .{ .arguments = &.{.string} };
pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result { pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result {
self.input_ = self.input_[0 .. self.input_.len - tui.egc_last(self.input_).len]; self.input = self.input[0 .. self.input.len - tui.egc_last(self.input).len];
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_delete_backwards_meta: Meta = .{ .description = "Delete backwards" }; pub const mini_mode_delete_backwards_meta = .{ .description = "Delete backwards" };
pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result { pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result {
return mini_mode_insert_bytes(self, ctx); return mini_mode_insert_bytes(self, ctx);
} }
pub const mini_mode_paste_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_paste_meta = .{ .arguments = &.{.string} };
}; };

View file

@ -82,14 +82,13 @@ fn insert_bytes(self: *Self, bytes: []const u8) void {
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn mini_mode_reset(self: *Self, _: Ctx) Result { pub fn mini_mode_reset(self: *Self, _: Ctx) Result {
self.input = null; self.input = null;
self.update_mini_mode_text(); self.update_mini_mode_text();
} }
pub const mini_mode_reset_meta: Meta = .{ .description = "Clear input" }; pub const mini_mode_reset_meta = .{ .description = "Clear input" };
pub fn mini_mode_cancel(self: *Self, _: Ctx) Result { pub fn mini_mode_cancel(self: *Self, _: Ctx) Result {
self.input = null; self.input = null;
@ -97,7 +96,7 @@ const cmds = struct {
self.goto(); self.goto();
command.executeName("exit_mini_mode", .{}) catch {}; command.executeName("exit_mini_mode", .{}) catch {};
} }
pub const mini_mode_cancel_meta: Meta = .{ .description = "Cancel input" }; pub const mini_mode_cancel_meta = .{ .description = "Cancel input" };
pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result { pub fn mini_mode_delete_backwards(self: *Self, _: Ctx) Result {
if (self.input) |linenum| { if (self.input) |linenum| {
@ -107,7 +106,7 @@ const cmds = struct {
self.goto(); self.goto();
} }
} }
pub const mini_mode_delete_backwards_meta: Meta = .{ .description = "Delete backwards" }; pub const mini_mode_delete_backwards_meta = .{ .description = "Delete backwards" };
pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result {
var keypress: usize = 0; var keypress: usize = 0;
@ -120,7 +119,7 @@ const cmds = struct {
self.update_mini_mode_text(); self.update_mini_mode_text();
self.goto(); self.goto();
} }
pub const mini_mode_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; pub const mini_mode_insert_code_point_meta = .{ .arguments = &.{.integer} };
pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result {
var bytes: []const u8 = undefined; var bytes: []const u8 = undefined;
@ -130,10 +129,5 @@ const cmds = struct {
self.update_mini_mode_text(); self.update_mini_mode_text();
self.goto(); self.goto();
} }
pub const mini_mode_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_insert_bytes_meta = .{ .arguments = &.{.string} };
pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result {
return mini_mode_insert_bytes(self, ctx);
}
pub const mini_mode_paste_meta: Meta = .{ .arguments = &.{.string} };
}; };

View file

@ -1,4 +1,3 @@
const std = @import("std");
const tp = @import("thespian"); const tp = @import("thespian");
const input = @import("input"); const input = @import("input");
@ -17,7 +16,6 @@ const Commands = command.Collection(cmds);
allocator: Allocator, allocator: Allocator,
key: [6]u8 = undefined, key: [6]u8 = undefined,
direction: Direction, direction: Direction,
operation_command: []const u8,
operation: Operation, operation: Operation,
commands: Commands = undefined, commands: Commands = undefined,
@ -32,18 +30,14 @@ const Operation = enum {
}; };
pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tui.MiniMode } { pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tui.MiniMode } {
var operation_command: []const u8 = undefined; var direction: Direction = undefined;
_ = ctx.args.match(.{tp.extract(&operation_command)}) catch return error.InvalidMoveToCharArgument; const select = if (tui.get_active_editor()) |editor| if (editor.get_primary().selection) |_| true else false else false;
_ = ctx.args.match(.{tp.extract(&direction)}) catch return error.InvalidMoveToCharArgument;
const direction: Direction = if (std.mem.indexOf(u8, operation_command, "_left")) |_| .left else .right;
const operation: Operation = if (tui.get_active_editor()) |editor| if (editor.get_primary().selection) |_| .select else .move else .move;
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
.direction = direction, .direction = direction,
.operation_command = try allocator.dupe(u8, operation_command), .operation = if (select) .select else .move,
.operation = operation,
}; };
try self.commands.init(self); try self.commands.init(self);
var mode = try keybind.mode("mini/move_to_char", allocator, .{ var mode = try keybind.mode("mini/move_to_char", allocator, .{
@ -55,7 +49,6 @@ pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tu
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
self.commands.deinit(); self.commands.deinit();
self.allocator.free(self.operation_command);
self.allocator.destroy(self); self.allocator.destroy(self);
} }
@ -77,14 +70,23 @@ pub fn receive(_: *Self, _: tp.pid_ref, _: tp.message) error{Exit}!bool {
} }
fn execute_operation(self: *Self, ctx: command.Context) command.Result { fn execute_operation(self: *Self, ctx: command.Context) command.Result {
try command.executeName(self.operation_command, ctx); const cmd = switch (self.direction) {
.left => switch (self.operation) {
.move => "move_to_char_left",
.select => "select_to_char_left",
},
.right => switch (self.operation) {
.move => "move_to_char_right",
.select => "select_to_char_right",
},
};
try command.executeName(cmd, ctx);
try command.executeName("exit_mini_mode", .{}); try command.executeName("exit_mini_mode", .{});
} }
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result {
@ -95,7 +97,7 @@ const cmds = struct {
const bytes = input.ucs32_to_utf8(&[_]u32{code_point}, &buf) catch return error.InvalidMoveToCharCodePoint; const bytes = input.ucs32_to_utf8(&[_]u32{code_point}, &buf) catch return error.InvalidMoveToCharCodePoint;
return self.execute_operation(command.fmt(.{buf[0..bytes]})); return self.execute_operation(command.fmt(.{buf[0..bytes]}));
} }
pub const mini_mode_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; pub const mini_mode_insert_code_point_meta = .{ .arguments = &.{.integer} };
pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result { pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result {
var bytes: []const u8 = undefined; var bytes: []const u8 = undefined;
@ -103,10 +105,10 @@ const cmds = struct {
return error.InvalidMoveToCharInsertBytesArgument; return error.InvalidMoveToCharInsertBytesArgument;
return self.execute_operation(ctx); return self.execute_operation(ctx);
} }
pub const mini_mode_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_insert_bytes_meta = .{ .arguments = &.{.string} };
pub fn mini_mode_cancel(_: *Self, _: Ctx) Result { pub fn mini_mode_cancel(_: *Self, _: Ctx) Result {
command.executeName("exit_mini_mode", .{}) catch {}; command.executeName("exit_mini_mode", .{}) catch {};
} }
pub const mini_mode_cancel_meta: Meta = .{ .description = "Cancel input" }; pub const mini_mode_cancel_meta = .{ .description = "Cancel input" };
}; };

View file

@ -272,58 +272,57 @@ const Commands = command.Collection(cmds);
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn palette_menu_top(self: *Self, _: Ctx) Result { pub fn palette_menu_top(self: *Self, _: Ctx) Result {
self.menu.select_first(); self.menu.select_first();
} }
pub const palette_menu_top_meta: Meta = .{}; pub const palette_menu_top_meta = .{};
pub fn palette_menu_down(self: *Self, _: Ctx) Result { pub fn palette_menu_down(self: *Self, _: Ctx) Result {
self.menu.select_down(); self.menu.select_down();
} }
pub const palette_menu_down_meta: Meta = .{}; pub const palette_menu_down_meta = .{};
pub fn palette_menu_up(self: *Self, _: Ctx) Result { pub fn palette_menu_up(self: *Self, _: Ctx) Result {
self.menu.select_up(); self.menu.select_up();
} }
pub const palette_menu_up_meta: Meta = .{}; pub const palette_menu_up_meta = .{};
pub fn palette_menu_pagedown(self: *Self, _: Ctx) Result { pub fn palette_menu_pagedown(self: *Self, _: Ctx) Result {
self.menu.select_last(); self.menu.select_last();
} }
pub const palette_menu_pagedown_meta: Meta = .{}; pub const palette_menu_pagedown_meta = .{};
pub fn palette_menu_pageup(self: *Self, _: Ctx) Result { pub fn palette_menu_pageup(self: *Self, _: Ctx) Result {
self.menu.select_first(); self.menu.select_first();
} }
pub const palette_menu_pageup_meta: Meta = .{}; pub const palette_menu_pageup_meta = .{};
pub fn palette_menu_activate(self: *Self, _: Ctx) Result { pub fn palette_menu_activate(self: *Self, _: Ctx) Result {
self.menu.activate_selected(); self.menu.activate_selected();
} }
pub const palette_menu_activate_meta: Meta = .{}; pub const palette_menu_activate_meta = .{};
pub fn palette_menu_activate_quick(self: *Self, _: Ctx) Result { pub fn palette_menu_activate_quick(self: *Self, _: Ctx) Result {
if (self.menu.selected orelse 0 > 0) self.menu.activate_selected(); if (self.menu.selected orelse 0 > 0) self.menu.activate_selected();
} }
pub const palette_menu_activate_quick_meta: Meta = .{}; pub const palette_menu_activate_quick_meta = .{};
pub fn palette_menu_cancel(self: *Self, _: Ctx) Result { pub fn palette_menu_cancel(self: *Self, _: Ctx) Result {
try self.cmd("exit_overlay_mode", .{}); try self.cmd("exit_overlay_mode", .{});
} }
pub const palette_menu_cancel_meta: Meta = .{}; pub const palette_menu_cancel_meta = .{};
pub fn overlay_delete_word_left(self: *Self, _: Ctx) Result { pub fn overlay_delete_word_left(self: *Self, _: Ctx) Result {
self.delete_word() catch |e| return tp.exit_error(e, @errorReturnTrace()); self.delete_word() catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const overlay_delete_word_left_meta: Meta = .{ .description = "Delete word to the left" }; pub const overlay_delete_word_left_meta = .{ .description = "Delete word to the left" };
pub fn overlay_delete_backwards(self: *Self, _: Ctx) Result { pub fn overlay_delete_backwards(self: *Self, _: Ctx) Result {
self.delete_code_point() catch |e| return tp.exit_error(e, @errorReturnTrace()); self.delete_code_point() catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const overlay_delete_backwards_meta: Meta = .{ .description = "Delete backwards" }; pub const overlay_delete_backwards_meta = .{ .description = "Delete backwards" };
pub fn overlay_insert_code_point(self: *Self, ctx: Ctx) Result { pub fn overlay_insert_code_point(self: *Self, ctx: Ctx) Result {
var egc: u32 = 0; var egc: u32 = 0;
@ -331,7 +330,7 @@ const cmds = struct {
return error.InvalidOpenRecentInsertCodePointArgument; return error.InvalidOpenRecentInsertCodePointArgument;
self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const overlay_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; pub const overlay_insert_code_point_meta = .{ .arguments = &.{.integer} };
pub fn overlay_insert_bytes(self: *Self, ctx: Ctx) Result { pub fn overlay_insert_bytes(self: *Self, ctx: Ctx) Result {
var bytes: []const u8 = undefined; var bytes: []const u8 = undefined;
@ -339,20 +338,20 @@ const cmds = struct {
return error.InvalidOpenRecentInsertBytesArgument; return error.InvalidOpenRecentInsertBytesArgument;
self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const overlay_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; pub const overlay_insert_bytes_meta = .{ .arguments = &.{.string} };
pub fn overlay_toggle_panel(self: *Self, _: Ctx) Result { pub fn overlay_toggle_panel(self: *Self, _: Ctx) Result {
return self.cmd_async("toggle_panel"); return self.cmd_async("toggle_panel");
} }
pub const overlay_toggle_panel_meta: Meta = .{}; pub const overlay_toggle_panel_meta = .{};
pub fn overlay_toggle_inputview(self: *Self, _: Ctx) Result { pub fn overlay_toggle_inputview(self: *Self, _: Ctx) Result {
return self.cmd_async("toggle_inputview"); return self.cmd_async("toggle_inputview");
} }
pub const overlay_toggle_inputview_meta: Meta = .{}; pub const overlay_toggle_inputview_meta = .{};
pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result { pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result {
return overlay_insert_bytes(self, ctx); return overlay_insert_bytes(self, ctx);
} }
pub const mini_mode_paste_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_paste_meta = .{ .arguments = &.{.string} };
}; };

View file

@ -366,7 +366,6 @@ pub fn Create(options: type) type {
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn palette_menu_down(self: *Self, _: Ctx) Result { pub fn palette_menu_down(self: *Self, _: Ctx) Result {
@ -384,7 +383,7 @@ pub fn Create(options: type) type {
self.menu.select_down(); self.menu.select_down();
self.selection_updated(); self.selection_updated();
} }
pub const palette_menu_down_meta: Meta = .{}; pub const palette_menu_down_meta = .{};
pub fn palette_menu_up(self: *Self, _: Ctx) Result { pub fn palette_menu_up(self: *Self, _: Ctx) Result {
if (self.menu.selected) |selected| { if (self.menu.selected) |selected| {
@ -399,7 +398,7 @@ pub fn Create(options: type) type {
self.menu.select_up(); self.menu.select_up();
self.selection_updated(); self.selection_updated();
} }
pub const palette_menu_up_meta: Meta = .{}; pub const palette_menu_up_meta = .{};
pub fn palette_menu_pagedown(self: *Self, _: Ctx) Result { pub fn palette_menu_pagedown(self: *Self, _: Ctx) Result {
if (self.total_items > self.view_rows) { if (self.total_items > self.view_rows) {
@ -411,7 +410,7 @@ pub fn Create(options: type) type {
self.menu.select_last(); self.menu.select_last();
self.selection_updated(); self.selection_updated();
} }
pub const palette_menu_pagedown_meta: Meta = .{}; pub const palette_menu_pagedown_meta = .{};
pub fn palette_menu_pageup(self: *Self, _: Ctx) Result { pub fn palette_menu_pageup(self: *Self, _: Ctx) Result {
if (self.view_pos > self.view_rows) if (self.view_pos > self.view_rows)
@ -422,7 +421,7 @@ pub fn Create(options: type) type {
self.menu.select_first(); self.menu.select_first();
self.selection_updated(); self.selection_updated();
} }
pub const palette_menu_pageup_meta: Meta = .{}; pub const palette_menu_pageup_meta = .{};
pub fn palette_menu_delete_item(self: *Self, _: Ctx) Result { pub fn palette_menu_delete_item(self: *Self, _: Ctx) Result {
if (@hasDecl(options, "delete_item")) { if (@hasDecl(options, "delete_item")) {
@ -437,33 +436,33 @@ pub fn Create(options: type) type {
} }
} }
} }
pub const palette_menu_delete_item_meta: Meta = .{}; pub const palette_menu_delete_item_meta = .{};
pub fn palette_menu_activate(self: *Self, _: Ctx) Result { pub fn palette_menu_activate(self: *Self, _: Ctx) Result {
self.menu.activate_selected(); self.menu.activate_selected();
} }
pub const palette_menu_activate_meta: Meta = .{}; pub const palette_menu_activate_meta = .{};
pub fn palette_menu_activate_quick(self: *Self, _: Ctx) Result { pub fn palette_menu_activate_quick(self: *Self, _: Ctx) Result {
if (self.menu.selected orelse 0 > 0) self.menu.activate_selected(); if (self.menu.selected orelse 0 > 0) self.menu.activate_selected();
} }
pub const palette_menu_activate_quick_meta: Meta = .{}; pub const palette_menu_activate_quick_meta = .{};
pub fn palette_menu_cancel(self: *Self, _: Ctx) Result { pub fn palette_menu_cancel(self: *Self, _: Ctx) Result {
if (@hasDecl(options, "cancel")) try options.cancel(self); if (@hasDecl(options, "cancel")) try options.cancel(self);
try self.cmd("exit_overlay_mode", .{}); try self.cmd("exit_overlay_mode", .{});
} }
pub const palette_menu_cancel_meta: Meta = .{}; pub const palette_menu_cancel_meta = .{};
pub fn overlay_delete_word_left(self: *Self, _: Ctx) Result { pub fn overlay_delete_word_left(self: *Self, _: Ctx) Result {
self.delete_word() catch |e| return tp.exit_error(e, @errorReturnTrace()); self.delete_word() catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const overlay_delete_word_left_meta: Meta = .{ .description = "Delete word to the left" }; pub const overlay_delete_word_left_meta = .{ .description = "Delete word to the left" };
pub fn overlay_delete_backwards(self: *Self, _: Ctx) Result { pub fn overlay_delete_backwards(self: *Self, _: Ctx) Result {
self.delete_code_point() catch |e| return tp.exit_error(e, @errorReturnTrace()); self.delete_code_point() catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const overlay_delete_backwards_meta: Meta = .{ .description = "Delete backwards" }; pub const overlay_delete_backwards_meta = .{ .description = "Delete backwards" };
pub fn overlay_insert_code_point(self: *Self, ctx: Ctx) Result { pub fn overlay_insert_code_point(self: *Self, ctx: Ctx) Result {
var egc: u32 = 0; var egc: u32 = 0;
@ -471,7 +470,7 @@ pub fn Create(options: type) type {
return error.InvalidPaletteInsertCodePointArgument; return error.InvalidPaletteInsertCodePointArgument;
self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.insert_code_point(egc) catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const overlay_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; pub const overlay_insert_code_point_meta = .{ .arguments = &.{.integer} };
pub fn overlay_insert_bytes(self: *Self, ctx: Ctx) Result { pub fn overlay_insert_bytes(self: *Self, ctx: Ctx) Result {
var bytes: []const u8 = undefined; var bytes: []const u8 = undefined;
@ -479,22 +478,22 @@ pub fn Create(options: type) type {
return error.InvalidPaletteInsertBytesArgument; return error.InvalidPaletteInsertBytesArgument;
self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace()); self.insert_bytes(bytes) catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const overlay_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; pub const overlay_insert_bytes_meta = .{ .arguments = &.{.string} };
pub fn overlay_toggle_panel(self: *Self, _: Ctx) Result { pub fn overlay_toggle_panel(self: *Self, _: Ctx) Result {
return self.cmd_async("toggle_panel"); return self.cmd_async("toggle_panel");
} }
pub const overlay_toggle_panel_meta: Meta = .{}; pub const overlay_toggle_panel_meta = .{};
pub fn overlay_toggle_inputview(self: *Self, _: Ctx) Result { pub fn overlay_toggle_inputview(self: *Self, _: Ctx) Result {
return self.cmd_async("toggle_inputview"); return self.cmd_async("toggle_inputview");
} }
pub const overlay_toggle_inputview_meta: Meta = .{}; pub const overlay_toggle_inputview_meta = .{};
pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result { pub fn mini_mode_paste(self: *Self, ctx: Ctx) Result {
return overlay_insert_bytes(self, ctx); return overlay_insert_bytes(self, ctx);
} }
pub const mini_mode_paste_meta: Meta = .{ .arguments = &.{.string} }; pub const mini_mode_paste_meta = .{ .arguments = &.{.string} };
}; };
}; };
} }

View file

@ -17,42 +17,33 @@ const Commands = command.Collection(cmds_);
const cmds_ = struct { const cmds_ = struct {
pub const Target = void; pub const Target = void;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
pub fn w(_: *void, _: Ctx) Result { pub fn w(_: *void, _: Ctx) Result {
try cmd("save_file", .{}); try cmd("save_file", .{});
} }
pub const w_meta: Meta = .{ .description = "w (write file)" }; pub const w_meta = .{ .description = "w (write file)" };
pub fn q(_: *void, _: Ctx) Result { pub fn q(_: *void, _: Ctx) Result {
try cmd("quit", .{}); try cmd("quit", .{});
} }
pub const q_meta: Meta = .{ .description = "q (quit)" }; pub const q_meta = .{ .description = "q (quit)" };
pub fn @"q!"(_: *void, _: Ctx) Result { pub fn @"q!"(_: *void, _: Ctx) Result {
try cmd("quit_without_saving", .{}); try cmd("quit_without_saving", .{});
} }
pub const @"q!_meta": Meta = .{ .description = "q! (quit without saving)" }; pub const @"q!_meta" = .{ .description = "q! (quit without saving)" };
pub fn wq(_: *void, _: Ctx) Result { pub fn wq(_: *void, _: Ctx) Result {
try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } })); try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } }));
} }
pub const wq_meta: Meta = .{ .description = "wq (write file and quit)" }; pub const wq_meta = .{ .description = "wq (write file and quit)" };
pub fn @"wq!"(_: *void, _: Ctx) Result { pub fn @"wq!"(_: *void, _: Ctx) Result {
cmd("save_file", .{}) catch {}; cmd("save_file", .{}) catch {};
try cmd("quit_without_saving", .{}); try cmd("quit_without_saving", .{});
} }
pub const @"wq!_meta": Meta = .{ .description = "wq! (write file and quit without saving)" }; pub const @"wq!_meta" = .{ .description = "wq! (write file and quit without saving)" };
pub fn move_begin_or_add_integer_argument_zero(_: *void, _: Ctx) Result {
return if (@import("keybind").current_integer_argument()) |_|
command.executeName("add_integer_argument_digit", command.fmt(.{0}))
else
command.executeName("move_begin", .{});
}
pub const move_begin_or_add_integer_argument_zero_meta: Meta = .{ .description = "Move cursor to beginning of line (vim)" };
pub fn enter_mode_at_next_char(self: *void, ctx: Ctx) Result { pub fn enter_mode_at_next_char(self: *void, ctx: Ctx) Result {
_ = self; // autofix _ = self; // autofix
@ -61,7 +52,7 @@ const cmds_ = struct {
return undefined; return undefined;
} }
pub const enter_mode_at_next_char_meta: Meta = .{ .description = "Move forward one char and change mode" }; pub const enter_mode_at_next_char_meta = .{ .description = "Move forward one char and change mode" };
pub fn enter_mode_on_newline_down(self: *void, ctx: Ctx) Result { pub fn enter_mode_on_newline_down(self: *void, ctx: Ctx) Result {
_ = self; // autofix _ = self; // autofix
@ -70,7 +61,7 @@ const cmds_ = struct {
return undefined; return undefined;
} }
pub const enter_mode_on_newline_down_meta: Meta = .{ .description = "Insert a newline and change mode" }; pub const enter_mode_on_newline_down_meta = .{ .description = "Insert a newline and change mode" };
pub fn enter_mode_on_newline_up(self: *void, ctx: Ctx) Result { pub fn enter_mode_on_newline_up(self: *void, ctx: Ctx) Result {
_ = self; // autofix _ = self; // autofix
@ -78,7 +69,7 @@ const cmds_ = struct {
//TODO //TODO
return undefined; return undefined;
} }
pub const enter_mode_on_newline_up_meta: Meta = .{ .description = "Insert a newline above the current line and change mode" }; pub const enter_mode_on_newline_up_meta = .{ .description = "Insert a newline above the current line and change mode" };
pub fn enter_mode_at_line_begin(self: *void, ctx: Ctx) Result { pub fn enter_mode_at_line_begin(self: *void, ctx: Ctx) Result {
_ = self; // autofix _ = self; // autofix
@ -87,7 +78,7 @@ const cmds_ = struct {
return undefined; return undefined;
} }
pub const enter_mode_at_line_begin_meta: Meta = .{ .description = "Goto line begin and change mode" }; pub const enter_mode_at_line_begin_meta = .{ .description = "Goto line begin and change mode" };
pub fn enter_mode_at_line_end(self: *void, ctx: Ctx) Result { pub fn enter_mode_at_line_end(self: *void, ctx: Ctx) Result {
_ = self; // autofix _ = self; // autofix
@ -95,7 +86,7 @@ const cmds_ = struct {
//TODO //TODO
return undefined; return undefined;
} }
pub const enter_mode_at_line_end_meta: Meta = .{ .description = "Goto line end and change mode" }; pub const enter_mode_at_line_end_meta = .{ .description = "Goto line end and change mode" };
pub fn copy_line(self: *void, ctx: Ctx) Result { pub fn copy_line(self: *void, ctx: Ctx) Result {
_ = self; // autofix _ = self; // autofix
@ -104,7 +95,7 @@ const cmds_ = struct {
return undefined; return undefined;
} }
pub const copy_line_meta: Meta = .{ .description = "Copies the current line" }; pub const copy_line_meta = .{ .description = "Copies the current line" };
pub fn delete_line(self: *void, ctx: Ctx) Result { pub fn delete_line(self: *void, ctx: Ctx) Result {
_ = self; // autofix _ = self; // autofix
@ -119,5 +110,5 @@ const cmds_ = struct {
//try self.update_buf(root); //try self.update_buf(root);
//self.clamp(); //self.clamp();
} }
pub const delete_line_meta: Meta = .{ .description = "Delete the current line without copying" }; pub const delete_line_meta = .{ .description = "Delete the current line without copying" };
}; };

View file

@ -8,17 +8,13 @@ const Plane = @import("renderer").Plane;
pub const Style = enum { none, grip }; pub const Style = enum { none, grip };
pub fn create(allocator: std.mem.Allocator, parent: Plane, config: []const u8, style: Style, event_handler: ?EventHandler) error{OutOfMemory}!Widget { pub fn create(allocator: std.mem.Allocator, parent: Plane, config: []const u8, style: Style, event_handler: ?EventHandler) !Widget {
var w = try WidgetList.createH(allocator, parent, "statusbar", .{ .static = 1 }); var w = try WidgetList.createH(allocator, parent, "statusbar", .{ .static = 1 });
if (style == .grip) w.after_render = render_grip; if (style == .grip) w.after_render = render_grip;
w.ctx = w; w.ctx = w;
var it = std.mem.splitScalar(u8, config, ' '); var it = std.mem.splitScalar(u8, config, ' ');
while (it.next()) |widget_name| { while (it.next()) |widget_name|
try w.add(status_widget.create(widget_name, allocator, w.plane, event_handler) catch |e| switch (e) { try w.add(try status_widget.create(widget_name, allocator, w.plane, event_handler) orelse continue);
error.OutOfMemory => return error.OutOfMemory,
error.WidgetInitFailed => null,
} orelse continue);
}
return w.widget(); return w.widget();
} }

View file

@ -6,24 +6,18 @@ const EventHandler = @import("EventHandler");
const Widget = @import("../Widget.zig"); const Widget = @import("../Widget.zig");
plane: Plane, plane: Plane,
layout_: Widget.Layout, layout: Widget.Layout,
on_event: ?EventHandler, on_event: ?EventHandler,
const Self = @This(); const Self = @This();
pub fn Create(comptime layout_: Widget.Layout) @import("widget.zig").CreateFunction { pub fn Create(comptime layout_: Widget.Layout) @import("widget.zig").CreateFunction {
return struct { return struct {
fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) @import("widget.zig").CreateError!Widget { fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
const layout__ = if (layout_ == .static) blk: {
if (arg) |str_size| {
const size = std.fmt.parseInt(usize, str_size, 10) catch break :blk layout_;
break :blk Widget.Layout{ .static = size };
} else break :blk layout_;
} else layout_;
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
.layout_ = layout__, .layout = layout_,
.on_event = event_handler, .on_event = event_handler,
}; };
return Widget.to(self); return Widget.to(self);
@ -37,7 +31,7 @@ pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
} }
pub fn layout(self: *Self) Widget.Layout { pub fn layout(self: *Self) Widget.Layout {
return self.layout_; return self.layout;
} }
pub fn render(self: *Self, theme: *const Widget.Theme) bool { pub fn render(self: *Self, theme: *const Widget.Theme) bool {

View file

@ -1,96 +0,0 @@
const std = @import("std");
const tp = @import("thespian");
const EventHandler = @import("EventHandler");
const Plane = @import("renderer").Plane;
const git = @import("git");
const Widget = @import("../Widget.zig");
const MessageFilter = @import("../MessageFilter.zig");
const tui = @import("../tui.zig");
const branch_symbol = "󰘬";
allocator: std.mem.Allocator,
plane: Plane,
branch: ?[]const u8 = null,
const Self = @This();
pub fn create(
allocator: std.mem.Allocator,
parent: Plane,
_: ?EventHandler,
_: ?[]const u8,
) @import("widget.zig").CreateError!Widget {
const self: *Self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
};
try tui.message_filters().add(MessageFilter.bind(self, receive_git));
git.workspace_path(0) catch {};
return Widget.to(self);
}
pub fn deinit(self: *Self, allocator: std.mem.Allocator) void {
if (self.branch) |p| self.allocator.free(p);
self.plane.deinit();
allocator.destroy(self);
}
fn receive_git(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.Error!bool {
return if (try match(m.buf, .{ "git", more }))
self.process_git(m)
else
false;
}
fn process_git(
self: *Self,
m: tp.message,
) MessageFilter.Error!bool {
var branch: []const u8 = undefined;
if (try match(m.buf, .{ any, any, "workspace_path", null_ })) {
// do nothing, we do not have a git workspace
} else if (try match(m.buf, .{ any, any, "workspace_path", string })) {
git.current_branch(0) catch {};
} else if (try match(m.buf, .{ any, any, "current_branch", extract(&branch) })) {
if (self.branch) |p| self.allocator.free(p);
self.branch = try self.allocator.dupe(u8, branch);
} else {
return false;
}
return true;
}
pub fn layout(self: *Self) Widget.Layout {
const branch = self.branch orelse return .{ .static = 0 };
var buf: [256]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
const writer = fbs.writer();
writer.print("{s} {s}", .{ branch_symbol, branch }) catch {};
const len = self.plane.egc_chunk_width(fbs.getWritten(), 0, 1);
return .{ .static = len };
}
pub fn render(self: *Self, theme: *const Widget.Theme) bool {
const branch = self.branch orelse return false;
self.plane.set_base_style(theme.editor);
self.plane.erase();
self.plane.home();
self.plane.set_style(theme.statusbar);
self.plane.fill(" ");
self.plane.home();
_ = self.plane.print("{s} {s}", .{ branch_symbol, branch }) catch {};
return false;
}
const match = cbor.match;
const more = cbor.more;
const null_ = cbor.null_;
const string = cbor.string;
const extract = cbor.extract;
const any = cbor.any;
const cbor = @import("cbor");

View file

@ -9,37 +9,24 @@ const Plane = @import("renderer").Plane;
const Widget = @import("../Widget.zig"); const Widget = @import("../Widget.zig");
const MessageFilter = @import("../MessageFilter.zig"); const MessageFilter = @import("../MessageFilter.zig");
const tui = @import("../tui.zig"); const tui = @import("../tui.zig");
const fonts = @import("../fonts.zig");
const DigitStyle = fonts.DigitStyle;
allocator: std.mem.Allocator, allocator: std.mem.Allocator,
plane: Plane, plane: Plane,
tick_timer: ?tp.Cancellable = null, tick_timer: ?tp.Cancellable = null,
on_event: ?EventHandler, on_event: ?EventHandler,
tz: zeit.timezone.TimeZone, tz: zeit.timezone.TimeZone,
style: ?DigitStyle,
const Self = @This(); const Self = @This();
pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
const style: ?DigitStyle = if (arg) |style| std.meta.stringToEnum(DigitStyle, style) orelse null else null; var env = std.process.getEnvMap(allocator) catch |e| return tp.exit_error(e, @errorReturnTrace());
var env = std.process.getEnvMap(allocator) catch |e| {
std.log.err("clock: std.process.getEnvMap failed with {any}", .{e});
return error.WidgetInitFailed;
};
defer env.deinit(); defer env.deinit();
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
.on_event = event_handler, .on_event = event_handler,
.tz = zeit.local(allocator, &env) catch |e| { .tz = zeit.local(allocator, &env) catch |e| return tp.exit_error(e, @errorReturnTrace()),
std.log.err("clock: zeit.local failed with {any}", .{e});
return error.WidgetInitFailed;
},
.style = style,
}; };
try tui.message_filters().add(MessageFilter.bind(self, receive_tick)); try tui.message_filters().add(MessageFilter.bind(self, receive_tick));
self.update_tick_timer(.init); self.update_tick_timer(.init);
@ -81,14 +68,7 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
const now = zeit.instant(.{ .timezone = &self.tz }) catch return false; const now = zeit.instant(.{ .timezone = &self.tz }) catch return false;
const dt = now.time(); const dt = now.time();
_ = self.plane.print("{d:0>2}:{d:0>2}", .{ dt.hour, dt.minute }) catch {};
var buf: [64]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
const writer = fbs.writer();
std.fmt.format(writer, "{d:0>2}:{d:0>2}", .{ dt.hour, dt.minute }) catch {};
const value_str = fbs.getWritten();
for (value_str, 0..) |_, i| _ = self.plane.putstr(fonts.get_digit_ascii(value_str[i .. i + 1], self.style orelse .ascii)) catch {};
return false; return false;
} }

View file

@ -18,7 +18,7 @@ rendered: [:0]const u8 = "",
const Self = @This(); const Self = @This();
pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
return Button.create_widget(Self, allocator, parent, .{ return Button.create_widget(Self, allocator, parent, .{
.ctx = .{}, .ctx = .{},
.label = "", .label = "",

View file

@ -7,7 +7,6 @@ const root = @import("root");
const Plane = @import("renderer").Plane; const Plane = @import("renderer").Plane;
const style = @import("renderer").style; const style = @import("renderer").style;
const styles = @import("renderer").styles;
const command = @import("command"); const command = @import("command");
const EventHandler = @import("EventHandler"); const EventHandler = @import("EventHandler");
@ -38,7 +37,7 @@ utf8_sanitized: bool = false,
const project_icon = ""; const project_icon = "";
const Self = @This(); const Self = @This();
pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
const btn = try Button.create(Self, allocator, parent, .{ const btn = try Button.create(Self, allocator, parent, .{
.ctx = .{ .ctx = .{
.allocator = allocator, .allocator = allocator,
@ -102,7 +101,7 @@ pub fn render(self: *Self, btn: *Button.State(Self), theme: *const Widget.Theme)
} }
fn render_mini_mode(plane: *Plane, theme: *const Widget.Theme) void { fn render_mini_mode(plane: *Plane, theme: *const Widget.Theme) void {
plane.off_styles(styles.italic); plane.off_styles(style.italic);
const mini_mode = tui.mini_mode() orelse return; const mini_mode = tui.mini_mode() orelse return;
_ = plane.print(" {s}", .{mini_mode.text}) catch {}; _ = plane.print(" {s}", .{mini_mode.text}) catch {};
if (mini_mode.cursor) |cursor| { if (mini_mode.cursor) |cursor| {
@ -130,7 +129,7 @@ fn render_mini_mode(plane: *Plane, theme: *const Widget.Theme) void {
// 󱑛 Content save cog // 󱑛 Content save cog
// 󰆔 Content save all // 󰆔 Content save all
fn render_normal(self: *Self, plane: *Plane, theme: *const Widget.Theme) void { fn render_normal(self: *Self, plane: *Plane, theme: *const Widget.Theme) void {
plane.on_styles(styles.italic); plane.on_styles(style.italic);
_ = plane.putstr(" ") catch {}; _ = plane.putstr(" ") catch {};
if (self.file_icon.len > 0 and tui.config().show_fileicons) { if (self.file_icon.len > 0 and tui.config().show_fileicons) {
self.render_file_icon(plane, theme); self.render_file_icon(plane, theme);
@ -142,7 +141,7 @@ fn render_normal(self: *Self, plane: *Plane, theme: *const Widget.Theme) void {
} }
fn render_detailed(self: *Self, plane: *Plane, theme: *const Widget.Theme) void { fn render_detailed(self: *Self, plane: *Plane, theme: *const Widget.Theme) void {
plane.on_styles(styles.italic); plane.on_styles(style.italic);
_ = plane.putstr(" ") catch {}; _ = plane.putstr(" ") catch {};
if (self.file_icon.len > 0 and tui.config().show_fileicons) { if (self.file_icon.len > 0 and tui.config().show_fileicons) {
self.render_file_icon(plane, theme); self.render_file_icon(plane, theme);

View file

@ -11,7 +11,7 @@ plane: Plane,
const Self = @This(); const Self = @This();
pub fn create(allocator: std.mem.Allocator, parent: Plane, _: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: std.mem.Allocator, parent: Plane, _: ?EventHandler) @import("widget.zig").CreateError!Widget {
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
@ -29,12 +29,9 @@ pub fn layout(_: *Self) Widget.Layout {
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf); var fbs = std.io.fixedBufferStream(&buf);
const writer = fbs.writer(); const writer = fbs.writer();
writer.print(" ", .{}) catch {}; writer.print("{}", .{keybind.current_key_event_sequence_fmt()}) catch {};
if (keybind.current_integer_argument()) |integer_argument|
writer.print("{}", .{integer_argument}) catch {};
writer.print("{} ", .{keybind.current_key_event_sequence_fmt()}) catch {};
const len = fbs.getWritten().len; const len = fbs.getWritten().len;
return .{ .static = if (len > 0) len else 0 }; return .{ .static = if (len > 0) len + 2 else 0 };
} }
pub fn render(self: *Self, theme: *const Widget.Theme) bool { pub fn render(self: *Self, theme: *const Widget.Theme) bool {
@ -44,9 +41,6 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
self.plane.set_style(theme.statusbar); self.plane.set_style(theme.statusbar);
self.plane.fill(" "); self.plane.fill(" ");
self.plane.home(); self.plane.home();
_ = self.plane.print(" ", .{}) catch {}; _ = self.plane.print(" {} ", .{keybind.current_key_event_sequence_fmt()}) catch {};
if (keybind.current_integer_argument()) |integer_argument|
_ = self.plane.print("{}", .{integer_argument}) catch {};
_ = self.plane.print("{} ", .{keybind.current_key_event_sequence_fmt()}) catch {};
return false; return false;
} }

View file

@ -29,7 +29,7 @@ const Self = @This();
const idle_msg = "🐶"; const idle_msg = "🐶";
pub const width = idle_msg.len + 20; pub const width = idle_msg.len + 20;
pub fn create(allocator: Allocator, parent: Plane, _: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: Allocator, parent: Plane, _: ?EventHandler) @import("widget.zig").CreateError!Widget {
const frame_rate = tp.env.get().num("frame-rate"); const frame_rate = tp.env.get().num("frame-rate");
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{

View file

@ -9,9 +9,6 @@ const EventHandler = @import("EventHandler");
const Widget = @import("../Widget.zig"); const Widget = @import("../Widget.zig");
const Button = @import("../Button.zig"); const Button = @import("../Button.zig");
const fonts = @import("../fonts.zig");
const DigitStyle = fonts.DigitStyle;
const utf8_sanitized_warning = "  UTF"; const utf8_sanitized_warning = "  UTF";
@ -22,32 +19,12 @@ buf: [256]u8 = undefined,
rendered: [:0]const u8 = "", rendered: [:0]const u8 = "",
eol_mode: Buffer.EolMode = .lf, eol_mode: Buffer.EolMode = .lf,
utf8_sanitized: bool = false, utf8_sanitized: bool = false,
padding: ?usize,
leader: ?Leader,
style: ?DigitStyle,
const Leader = enum {
space,
zero,
};
const Self = @This(); const Self = @This();
pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
const padding: ?usize, const leader: ?Leader, const style: ?DigitStyle = if (arg) |fmt| blk: {
var it = std.mem.splitScalar(u8, fmt, ',');
break :blk .{
if (it.next()) |size| std.fmt.parseInt(usize, size, 10) catch null else null,
if (it.next()) |leader| std.meta.stringToEnum(Leader, leader) orelse null else null,
if (it.next()) |style| std.meta.stringToEnum(DigitStyle, style) orelse null else null,
};
} else .{ null, null, null };
return Button.create_widget(Self, allocator, parent, .{ return Button.create_widget(Self, allocator, parent, .{
.ctx = .{ .ctx = .{},
.padding = padding,
.leader = leader,
.style = style,
},
.label = "", .label = "",
.on_click = on_click, .on_click = on_click,
.on_layout = layout, .on_layout = layout,
@ -90,30 +67,11 @@ fn format(self: *Self) void {
.lf => "", .lf => "",
.crlf => " [␍␊]", .crlf => " [␍␊]",
}; };
std.fmt.format(writer, "{s} Ln ", .{eol_mode}) catch {}; std.fmt.format(writer, "{s} Ln {d}, Col {d} ", .{ eol_mode, self.line + 1, self.column + 1 }) catch {};
self.format_count(writer, self.line + 1, self.padding orelse 0) catch {};
std.fmt.format(writer, ", Col ", .{}) catch {};
self.format_count(writer, self.column + 1, self.padding orelse 0) catch {};
std.fmt.format(writer, " ", .{}) catch {};
self.rendered = @ptrCast(fbs.getWritten()); self.rendered = @ptrCast(fbs.getWritten());
self.buf[self.rendered.len] = 0; self.buf[self.rendered.len] = 0;
} }
fn format_count(self: *Self, writer: anytype, value: usize, width: usize) !void {
var buf: [64]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
const writer_ = fbs.writer();
try std.fmt.format(writer_, "{d}", .{value});
const value_str = fbs.getWritten();
const char: []const u8 = switch (self.leader orelse .space) {
.space => " ",
.zero => "0",
};
for (0..(@max(value_str.len, width) - value_str.len)) |_| try writer.writeAll(fonts.get_digit_ascii(char, self.style orelse .ascii));
for (value_str, 0..) |_, i| try writer.writeAll(fonts.get_digit_ascii(value_str[i .. i + 1], self.style orelse .ascii));
}
pub fn receive(self: *Self, _: *Button.State(Self), _: tp.pid_ref, m: tp.message) error{Exit}!bool { pub fn receive(self: *Self, _: *Button.State(Self), _: tp.pid_ref, m: tp.message) error{Exit}!bool {
var eol_mode: Buffer.EolModeTag = @intFromEnum(Buffer.EolMode.lf); var eol_mode: Buffer.EolModeTag = @intFromEnum(Buffer.EolMode.lf);
if (try m.match(.{ "E", "pos", tp.extract(&self.lines), tp.extract(&self.line), tp.extract(&self.column) })) { if (try m.match(.{ "E", "pos", tp.extract(&self.lines), tp.extract(&self.line), tp.extract(&self.column) })) {

View file

@ -26,7 +26,7 @@ const Level = enum {
err, err,
}; };
pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
@ -35,7 +35,7 @@ pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?Event
}; };
logview.init(allocator); logview.init(allocator);
try tui.message_filters().add(MessageFilter.bind(self, receive_log)); try tui.message_filters().add(MessageFilter.bind(self, receive_log));
log.subscribe() catch return error.WidgetInitFailed; try log.subscribe();
return Widget.to(self); return Widget.to(self);
} }

View file

@ -3,7 +3,6 @@ const Allocator = std.mem.Allocator;
const Plane = @import("renderer").Plane; const Plane = @import("renderer").Plane;
const style = @import("renderer").style; const style = @import("renderer").style;
const styles = @import("renderer").styles;
const command = @import("command"); const command = @import("command");
const EventHandler = @import("EventHandler"); const EventHandler = @import("EventHandler");
@ -12,7 +11,7 @@ const Button = @import("../Button.zig");
const tui = @import("../tui.zig"); const tui = @import("../tui.zig");
const CreateError = @import("widget.zig").CreateError; const CreateError = @import("widget.zig").CreateError;
pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) CreateError!Widget { pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!Widget {
return Button.create_widget(void, allocator, parent, .{ return Button.create_widget(void, allocator, parent, .{
.ctx = {}, .ctx = {},
.label = tui.get_mode(), .label = tui.get_mode(),
@ -53,7 +52,7 @@ pub fn render(_: *void, self: *Button.State(void), theme: *const Widget.Theme) b
self.plane.set_style(style_label); self.plane.set_style(style_label);
self.plane.fill(" "); self.plane.fill(" ");
self.plane.home(); self.plane.home();
self.plane.on_styles(styles.bold); self.plane.on_styles(style.bold);
var buf: [31:0]u8 = undefined; var buf: [31:0]u8 = undefined;
if (!is_mini_mode() and !is_overlay_mode()) { if (!is_mini_mode() and !is_overlay_mode()) {
render_logo(self, theme, style_label); render_logo(self, theme, style_label);
@ -61,7 +60,7 @@ pub fn render(_: *void, self: *Button.State(void), theme: *const Widget.Theme) b
_ = self.plane.putstr(" ") catch {}; _ = self.plane.putstr(" ") catch {};
} }
self.plane.set_style(style_label); self.plane.set_style(style_label);
self.plane.on_styles(styles.bold); self.plane.on_styles(style.bold);
_ = self.plane.putstr(std.fmt.bufPrintZ(&buf, "{s} ", .{tui.get_mode()}) catch return false) catch {}; _ = self.plane.putstr(std.fmt.bufPrintZ(&buf, "{s} ", .{tui.get_mode()}) catch return false) catch {};
if (is_mini_mode()) if (is_mini_mode())
render_separator(self, theme); render_separator(self, theme);

View file

@ -19,7 +19,7 @@ const Self = @This();
pub const width = 8; pub const width = 8;
pub fn create(allocator: Allocator, parent: Plane, _: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: Allocator, parent: Plane, _: ?EventHandler) @import("widget.zig").CreateError!Widget {
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),

View file

@ -18,7 +18,7 @@ on_event: ?EventHandler,
const Self = @This(); const Self = @This();
pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),

View file

@ -52,7 +52,7 @@ const @"style.config" = struct {
}; };
pub const Style = @"style.config"; pub const Style = @"style.config";
pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, _: ?[]const u8) @import("widget.zig").CreateError!Widget { pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
const self = try allocator.create(TabBar); const self = try allocator.create(TabBar);
self.* = try TabBar.init(allocator, parent, event_handler); self.* = try TabBar.init(allocator, parent, event_handler);
return Widget.to(self); return Widget.to(self);
@ -428,7 +428,7 @@ const Tab = struct {
const spacer = struct { const spacer = struct {
plane: Plane, plane: Plane,
layout_: Widget.Layout, layout: Widget.Layout,
on_event: ?EventHandler, on_event: ?EventHandler,
content: []const u8, content: []const u8,
fg: colors, fg: colors,
@ -447,7 +447,7 @@ const spacer = struct {
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent), .plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
.layout_ = .{ .static = self.plane.egc_chunk_width(content, 0, 1) }, .layout = .{ .static = self.plane.egc_chunk_width(content, 0, 1) },
.on_event = event_handler, .on_event = event_handler,
.content = content, .content = content,
.fg = fg, .fg = fg,
@ -462,7 +462,7 @@ const spacer = struct {
} }
pub fn layout(self: *Self) Widget.Layout { pub fn layout(self: *Self) Widget.Layout {
return self.layout_; return self.layout;
} }
pub fn render(self: *Self, theme: *const Widget.Theme) bool { pub fn render(self: *Self, theme: *const Widget.Theme) bool {

View file

@ -19,27 +19,18 @@ const widgets = std.static_string_map.StaticStringMap(CreateFunction).initCompti
.{ "clock", @import("clock.zig").create }, .{ "clock", @import("clock.zig").create },
.{ "keybind", @import("keybindstate.zig").create }, .{ "keybind", @import("keybindstate.zig").create },
.{ "tabs", @import("tabs.zig").create }, .{ "tabs", @import("tabs.zig").create },
.{ "branch", @import("branch.zig").create },
}); });
pub const CreateError = error{ OutOfMemory, WidgetInitFailed }; pub const CreateError = error{ OutOfMemory, Exit };
pub const CreateFunction = *const fn (allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) CreateError!Widget; pub const CreateFunction = *const fn (allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!Widget;
pub fn create(descriptor: []const u8, allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!?Widget {
var it = std.mem.splitScalar(u8, descriptor, ':');
const name = it.next() orelse {
const logger = log.logger("statusbar");
logger.print_err("config", "bad widget descriptor \"{s}\" (see log)", .{descriptor});
return null;
};
const arg = it.next();
pub fn create(name: []const u8, allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!?Widget {
const create_ = widgets.get(name) orelse { const create_ = widgets.get(name) orelse {
const logger = log.logger("statusbar"); const logger = log.logger("statusbar");
logger.print_err("config", "unknown widget \"{s}\" (see log)", .{name}); logger.print_err("config", "unknown widget \"{s}\" (see log)", .{name});
log_widgets(logger); log_widgets(logger);
return null; return null;
}; };
return try create_(allocator, parent, event_handler, arg); return try create_(allocator, parent, event_handler);
} }
fn log_widgets(logger: anytype) void { fn log_widgets(logger: anytype) void {

View file

@ -12,7 +12,6 @@ pub const renderer = @import("renderer");
const command = @import("command"); const command = @import("command");
const EventHandler = @import("EventHandler"); const EventHandler = @import("EventHandler");
const keybind = @import("keybind"); const keybind = @import("keybind");
const syntax = @import("syntax");
const Widget = @import("Widget.zig"); const Widget = @import("Widget.zig");
const MessageFilter = @import("MessageFilter.zig"); const MessageFilter = @import("MessageFilter.zig");
@ -21,31 +20,29 @@ const MainView = @import("mainview.zig");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
allocator: Allocator, allocator: Allocator,
rdr_: renderer, rdr: renderer,
config_: @import("config"), config: @import("config"),
highlight_columns_: []u16,
frame_time: usize, // in microseconds frame_time: usize, // in microseconds
frame_clock: tp.metronome, frame_clock: tp.metronome,
frame_clock_running: bool = false, frame_clock_running: bool = false,
frame_last_time: i64 = 0, frame_last_time: i64 = 0,
receiver: Receiver, receiver: Receiver,
mainview_: ?Widget = null, mainview: ?Widget = null,
message_filters_: MessageFilter.List, message_filters: MessageFilter.List,
input_mode_: ?Mode = null, input_mode: ?Mode = null,
delayed_init_done: bool = false, delayed_init_done: bool = false,
delayed_init_input_mode: ?Mode = null, delayed_init_input_mode: ?Mode = null,
input_mode_outer_: ?Mode = null, input_mode_outer: ?Mode = null,
input_listeners_: EventHandler.List, input_listeners: EventHandler.List,
keyboard_focus: ?Widget = null, keyboard_focus: ?Widget = null,
mini_mode_: ?MiniMode = null, mini_mode: ?MiniMode = null,
hover_focus: ?*Widget = null, hover_focus: ?*Widget = null,
last_hover_x: c_int = -1, last_hover_x: c_int = -1,
last_hover_y: c_int = -1, last_hover_y: c_int = -1,
commands: Commands = undefined, commands: Commands = undefined,
logger: log.Logger, logger: log.Logger,
drag_source: ?*Widget = null, drag_source: ?*Widget = null,
theme_: Widget.Theme, theme: Widget.Theme,
parsed_theme: ?std.json.Parsed(Widget.Theme),
idle_frame_count: usize = 0, idle_frame_count: usize = 0,
unrendered_input_events_count: usize = 0, unrendered_input_events_count: usize = 0,
init_timer: ?tp.timeout, init_timer: ?tp.timeout,
@ -56,10 +53,9 @@ render_pending: bool = false,
keepalive_timer: ?tp.Cancellable = null, keepalive_timer: ?tp.Cancellable = null,
mouse_idle_timer: ?tp.Cancellable = null, mouse_idle_timer: ?tp.Cancellable = null,
default_cursor: keybind.CursorShape = .default, default_cursor: keybind.CursorShape = .default,
fontface_: []const u8 = "", fontface: []const u8 = "",
fontfaces_: std.ArrayListUnmanaged([]const u8) = .{}, fontfaces: std.ArrayListUnmanaged([]const u8) = .{},
enable_mouse_idle_timer: bool = false, enable_mouse_idle_timer: bool = false,
query_cache_: *syntax.QueryCache,
const keepalive = std.time.us_per_day * 365; // one year const keepalive = std.time.us_per_day * 365; // one year
const idle_frames = 0; const idle_frames = 0;
@ -86,96 +82,61 @@ fn start(args: StartArgs) tp.result {
tp.receive(&self.receiver); tp.receive(&self.receiver);
} }
const InitError = error{ fn init(allocator: Allocator) !*Self {
OutOfMemory,
UnknownTheme,
ThespianMetronomeInitFailed,
ThespianMetronomeStartFailed,
ThespianTimeoutInitFailed,
ThespianSignalInitFailed,
ThespianSpawnFailed,
} || renderer.Error ||
root.ConfigDirError ||
root.ConfigWriteError ||
keybind.LoadError;
fn init(allocator: Allocator) InitError!*Self {
var conf, const conf_bufs = root.read_config(@import("config"), allocator); var conf, const conf_bufs = root.read_config(@import("config"), allocator);
defer root.free_config(allocator, conf_bufs); defer root.free_config(allocator, conf_bufs);
if (conf.start_debugger_on_crash) const theme_ = get_theme_by_name(conf.theme) orelse get_theme_by_name("dark_modern") orelse return tp.exit("unknown theme");
tp.install_debugger();
const theme_, const parsed_theme = get_theme_by_name(allocator, conf.theme) orelse get_theme_by_name(allocator, "dark_modern") orelse return error.UnknownTheme;
conf.theme = theme_.name; conf.theme = theme_.name;
conf.whitespace_mode = try allocator.dupe(u8, conf.whitespace_mode); conf.whitespace_mode = try allocator.dupe(u8, conf.whitespace_mode);
conf.input_mode = try allocator.dupe(u8, conf.input_mode); conf.input_mode = try allocator.dupe(u8, conf.input_mode);
conf.top_bar = try allocator.dupe(u8, conf.top_bar); conf.top_bar = try allocator.dupe(u8, conf.top_bar);
conf.bottom_bar = try allocator.dupe(u8, conf.bottom_bar); conf.bottom_bar = try allocator.dupe(u8, conf.bottom_bar);
conf.include_files = try allocator.dupe(u8, conf.include_files); conf.include_files = try allocator.dupe(u8, conf.include_files);
conf.highlight_columns = try allocator.dupe(u8, conf.highlight_columns);
if (build_options.gui) conf.enable_terminal_cursor = false; if (build_options.gui) conf.enable_terminal_cursor = false;
const frame_rate: usize = @intCast(tp.env.get().num("frame-rate")); const frame_rate: usize = @intCast(tp.env.get().num("frame-rate"));
if (frame_rate != 0) if (frame_rate != 0)
conf.frame_rate = frame_rate; conf.frame_rate = frame_rate;
tp.env.get().num_set("frame-rate", @intCast(conf.frame_rate)); tp.env.get().num_set("frame-rate", @intCast(conf.frame_rate));
tp.env.get().num_set("lsp-request-timeout", @intCast(conf.lsp_request_timeout));
const frame_time = std.time.us_per_s / conf.frame_rate; const frame_time = std.time.us_per_s / conf.frame_rate;
const frame_clock = try tp.metronome.init(frame_time); const frame_clock = try tp.metronome.init(frame_time);
const hl_cols: usize = blk: {
var it = std.mem.splitScalar(u8, conf.highlight_columns, ' ');
var idx: usize = 0;
while (it.next()) |_|
idx += 1;
break :blk idx;
};
var self = try allocator.create(Self); var self = try allocator.create(Self);
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
.config_ = conf, .config = conf,
.highlight_columns_ = try allocator.alloc(u16, hl_cols), .rdr = try renderer.init(allocator, self, tp.env.get().is("no-alternate"), dispatch_initialized),
.rdr_ = try renderer.init(allocator, self, tp.env.get().is("no-alternate"), dispatch_initialized),
.frame_time = frame_time, .frame_time = frame_time,
.frame_clock = frame_clock, .frame_clock = frame_clock,
.frame_clock_running = true, .frame_clock_running = true,
.receiver = Receiver.init(receive, self), .receiver = Receiver.init(receive, self),
.message_filters_ = MessageFilter.List.init(allocator), .message_filters = MessageFilter.List.init(allocator),
.input_listeners_ = EventHandler.List.init(allocator), .input_listeners = EventHandler.List.init(allocator),
.logger = log.logger("tui"), .logger = log.logger("tui"),
.init_timer = if (build_options.gui) null else try tp.timeout.init_ms(init_delay, tp.message.fmt( .init_timer = if (build_options.gui) null else try tp.timeout.init_ms(init_delay, tp.message.fmt(
.{"init"}, .{"init"},
)), )),
.theme_ = theme_, .theme = theme_,
.no_sleep = tp.env.get().is("no-sleep"), .no_sleep = tp.env.get().is("no-sleep"),
.query_cache_ = try syntax.QueryCache.create(allocator, .{}),
.parsed_theme = parsed_theme,
}; };
instance_ = self; instance_ = self;
defer instance_ = null; defer instance_ = null;
var it = std.mem.splitScalar(u8, conf.highlight_columns, ' ');
var idx: usize = 0;
while (it.next()) |arg| {
self.highlight_columns_[idx] = std.fmt.parseInt(u16, arg, 10) catch 0;
idx += 1;
}
self.default_cursor = std.meta.stringToEnum(keybind.CursorShape, conf.default_cursor) orelse .default; self.default_cursor = std.meta.stringToEnum(keybind.CursorShape, conf.default_cursor) orelse .default;
self.config_.default_cursor = @tagName(self.default_cursor); self.config.default_cursor = @tagName(self.default_cursor);
self.rdr_.handler_ctx = self; self.rdr.handler_ctx = self;
self.rdr_.dispatch_input = dispatch_input; self.rdr.dispatch_input = dispatch_input;
self.rdr_.dispatch_mouse = dispatch_mouse; self.rdr.dispatch_mouse = dispatch_mouse;
self.rdr_.dispatch_mouse_drag = dispatch_mouse_drag; self.rdr.dispatch_mouse_drag = dispatch_mouse_drag;
self.rdr_.dispatch_event = dispatch_event; self.rdr.dispatch_event = dispatch_event;
try self.rdr_.run(); try self.rdr.run();
try project_manager.start(); try project_manager.start();
try frame_clock.start(); try frame_clock.start();
try self.commands.init(self); try self.commands.init(self);
try keybind.init();
errdefer self.deinit(); errdefer self.deinit();
switch (builtin.os.tag) { switch (builtin.os.tag) {
.windows => { .windows => {
@ -185,7 +146,7 @@ fn init(allocator: Allocator) InitError!*Self {
try self.listen_sigwinch(); try self.listen_sigwinch();
}, },
} }
self.mainview_ = try MainView.create(allocator); self.mainview = try MainView.create(allocator);
resize(); resize();
self.set_terminal_style(); self.set_terminal_style();
try save_config(); try save_config();
@ -197,20 +158,20 @@ fn init(allocator: Allocator) InitError!*Self {
return self; return self;
} }
fn init_input_namespace(self: *Self) InitError!void { fn init_input_namespace(self: *Self) !void {
var mode_parts = std.mem.splitScalar(u8, self.config_.input_mode, '/'); var mode_parts = std.mem.splitScalar(u8, self.config.input_mode, '/');
const namespace_name = mode_parts.first(); const namespace_name = mode_parts.first();
keybind.set_namespace(namespace_name) catch { keybind.set_namespace(namespace_name) catch {
self.logger.print_err("keybind", "unknown mode {s}", .{namespace_name}); self.logger.print_err("keybind", "unknown mode {s}", .{namespace_name});
try keybind.set_namespace("flow"); try keybind.set_namespace("flow");
self.config_.input_mode = "flow"; self.config.input_mode = "flow";
try save_config(); try save_config();
}; };
} }
fn init_delayed(self: *Self) command.Result { fn init_delayed(self: *Self) !void {
self.delayed_init_done = true; self.delayed_init_done = true;
if (self.input_mode_) |_| {} else { if (self.input_mode) |_| {} else {
if (self.delayed_init_input_mode) |delayed_init_input_mode| { if (self.delayed_init_input_mode) |delayed_init_input_mode| {
try enter_input_mode(self, delayed_init_input_mode); try enter_input_mode(self, delayed_init_input_mode);
self.delayed_init_input_mode = null; self.delayed_init_input_mode = null;
@ -231,32 +192,31 @@ fn deinit(self: *Self) void {
t.deinit(); t.deinit();
self.keepalive_timer = null; self.keepalive_timer = null;
} }
if (self.input_mode_) |*m| { if (self.input_mode) |*m| {
m.deinit(); m.deinit();
self.input_mode_ = null; self.input_mode = null;
} }
if (self.delayed_init_input_mode) |*m| { if (self.delayed_init_input_mode) |*m| {
m.deinit(); m.deinit();
self.delayed_init_input_mode = null; self.delayed_init_input_mode = null;
} }
self.commands.deinit(); self.commands.deinit();
if (self.mainview_) |*mv| mv.deinit(self.allocator); if (self.mainview) |*mv| mv.deinit(self.allocator);
self.message_filters_.deinit(); self.message_filters.deinit();
self.input_listeners_.deinit(); self.input_listeners.deinit();
if (self.frame_clock_running) if (self.frame_clock_running)
self.frame_clock.stop() catch {}; self.frame_clock.stop() catch {};
if (self.sigwinch_signal) |sig| sig.deinit(); if (self.sigwinch_signal) |sig| sig.deinit();
self.frame_clock.deinit(); self.frame_clock.deinit();
self.rdr_.stop(); self.rdr.stop();
self.rdr_.deinit(); self.rdr.deinit();
self.logger.deinit(); self.logger.deinit();
self.query_cache_.deinit();
self.allocator.destroy(self); self.allocator.destroy(self);
} }
fn listen_sigwinch(self: *Self) error{ThespianSignalInitFailed}!void { fn listen_sigwinch(self: *Self) tp.result {
if (self.sigwinch_signal) |old| old.deinit(); if (self.sigwinch_signal) |old| old.deinit();
self.sigwinch_signal = try tp.signal.init(std.posix.SIG.WINCH, tp.message.fmt(.{"sigwinch"})); self.sigwinch_signal = tp.signal.init(std.posix.SIG.WINCH, tp.message.fmt(.{"sigwinch"})) catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
fn update_mouse_idle_timer(self: *Self) void { fn update_mouse_idle_timer(self: *Self) void {
@ -293,7 +253,7 @@ fn receive(self: *Self, from: tp.pid_ref, m: tp.message) tp.result {
fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
if (try m.match(.{ "RDR", tp.more })) { if (try m.match(.{ "RDR", tp.more })) {
self.rdr_.process_renderer_event(m.buf) catch |e| switch (e) { self.rdr.process_renderer_event(m.buf) catch |e| switch (e) {
error.UnexpectedRendererEvent => return tp.unexpected(m), error.UnexpectedRendererEvent => return tp.unexpected(m),
else => return e, else => return e,
}; };
@ -303,7 +263,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
return; return;
} }
if (self.message_filters_.filter(from, m) catch |e| return self.logger.err("filter", e)) if (self.message_filters.filter(from, m) catch |e| return self.logger.err("filter", e))
return; return;
var cmd: []const u8 = undefined; var cmd: []const u8 = undefined;
@ -342,7 +302,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
if (builtin.os.tag != .windows) if (builtin.os.tag != .windows)
if (try m.match(.{"sigwinch"})) { if (try m.match(.{"sigwinch"})) {
try self.listen_sigwinch(); try self.listen_sigwinch();
self.rdr_.sigwinch() catch |e| return self.logger.err("query_resize", e); self.rdr.sigwinch() catch |e| return self.logger.err("query_resize", e);
return; return;
}; };
@ -430,14 +390,14 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
var fontface_: []const u8 = undefined; var fontface_: []const u8 = undefined;
if (try m.match(.{ "fontface", "current", tp.extract(&fontface_) })) { if (try m.match(.{ "fontface", "current", tp.extract(&fontface_) })) {
if (self.fontface_.len > 0) self.allocator.free(self.fontface_); if (self.fontface.len > 0) self.allocator.free(self.fontface);
self.fontface_ = ""; self.fontface = "";
self.fontface_ = try self.allocator.dupe(u8, fontface_); self.fontface = try self.allocator.dupe(u8, fontface_);
return; return;
} }
if (try m.match(.{ "fontface", tp.extract(&fontface_) })) { if (try m.match(.{ "fontface", tp.extract(&fontface_) })) {
try self.fontfaces_.append(self.allocator, try self.allocator.dupe(u8, fontface_)); try self.fontfaces.append(self.allocator, try self.allocator.dupe(u8, fontface_));
return; return;
} }
@ -460,20 +420,20 @@ fn render(self: *Self) void {
{ {
const frame = tracy.initZone(@src(), .{ .name = "tui update" }); const frame = tracy.initZone(@src(), .{ .name = "tui update" });
defer frame.deinit(); defer frame.deinit();
if (self.mainview_) |mv| mv.update(); if (self.mainview) |mv| mv.update();
} }
const more = ret: { const more = ret: {
const frame = tracy.initZone(@src(), .{ .name = "tui render" }); const frame = tracy.initZone(@src(), .{ .name = "tui render" });
defer frame.deinit(); defer frame.deinit();
self.rdr_.stdplane().erase(); self.rdr.stdplane().erase();
break :ret if (self.mainview_) |mv| mv.render(&self.theme_) else false; break :ret if (self.mainview) |mv| mv.render(&self.theme) else false;
}; };
{ {
const frame = tracy.initZone(@src(), .{ .name = renderer.log_name ++ " render" }); const frame = tracy.initZone(@src(), .{ .name = renderer.log_name ++ " render" });
defer frame.deinit(); defer frame.deinit();
self.rdr_.render() catch |e| self.logger.err("render", e); self.rdr.render() catch |e| self.logger.err("render", e);
tracy.frameMark(); tracy.frameMark();
} }
@ -497,13 +457,13 @@ fn render(self: *Self) void {
} }
fn active_event_handler(self: *Self) ?EventHandler { fn active_event_handler(self: *Self) ?EventHandler {
const mode = self.input_mode_ orelse return null; const mode = self.input_mode orelse return null;
return mode.event_handler orelse mode.input_handler; return mode.event_handler orelse mode.input_handler;
} }
fn dispatch_flush_input_event(self: *Self) error{ Exit, NoSpaceLeft }!void { fn dispatch_flush_input_event(self: *Self) !void {
var buf: [32]u8 = undefined; var buf: [32]u8 = undefined;
const mode = self.input_mode_ orelse return; const mode = self.input_mode orelse return;
try mode.input_handler.send(tp.self_pid(), try tp.message.fmtbuf(&buf, .{"F"})); try mode.input_handler.send(tp.self_pid(), try tp.message.fmtbuf(&buf, .{"F"}));
if (mode.event_handler) |eh| try eh.send(tp.self_pid(), try tp.message.fmtbuf(&buf, .{"F"})); if (mode.event_handler) |eh| try eh.send(tp.self_pid(), try tp.message.fmtbuf(&buf, .{"F"}));
} }
@ -520,14 +480,14 @@ fn dispatch_input(ctx: *anyopaque, cbor_msg: []const u8) void {
const m: tp.message = .{ .buf = cbor_msg }; const m: tp.message = .{ .buf = cbor_msg };
const from = tp.self_pid(); const from = tp.self_pid();
self.unrendered_input_events_count += 1; self.unrendered_input_events_count += 1;
self.input_listeners_.send(from, m) catch {}; self.input_listeners.send(from, m) catch {};
if (self.keyboard_focus) |w| if (self.keyboard_focus) |w|
if (w.send(from, m) catch |e| ret: { if (w.send(from, m) catch |e| ret: {
self.logger.err("focus", e); self.logger.err("focus", e);
break :ret false; break :ret false;
}) })
return; return;
if (self.input_mode_) |mode| if (self.input_mode) |mode|
mode.input_handler.send(from, m) catch |e| self.logger.err("input handler", e); mode.input_handler.send(from, m) catch |e| self.logger.err("input handler", e);
} }
@ -575,7 +535,7 @@ fn find_coord_widget(self: *Self, y: usize, x: usize) ?*Widget {
} }
}; };
var ctx: Ctx = .{ .y = y, .x = x }; var ctx: Ctx = .{ .y = y, .x = x };
if (self.mainview_) |*mv| _ = mv.walk(&ctx, Ctx.find); if (self.mainview) |*mv| _ = mv.walk(&ctx, Ctx.find);
return ctx.widget; return ctx.widget;
} }
@ -592,7 +552,7 @@ fn is_live_widget_ptr(self: *Self, w_: *Widget) bool {
} }
}; };
var ctx: Ctx = .{ .w = w_ }; var ctx: Ctx = .{ .w = w_ };
return if (self.mainview_) |*mv| mv.walk(&ctx, Ctx.find) else false; return if (self.mainview) |*mv| mv.walk(&ctx, Ctx.find) else false;
} }
fn send_widgets(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool { fn send_widgets(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool {
@ -601,7 +561,7 @@ fn send_widgets(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool {
tp.trace(tp.channel.widget, m); tp.trace(tp.channel.widget, m);
return if (self.keyboard_focus) |w| return if (self.keyboard_focus) |w|
w.send(from, m) w.send(from, m)
else if (self.mainview_) |mv| else if (self.mainview) |mv|
mv.send(from, m) mv.send(from, m)
else else
false; false;
@ -609,7 +569,7 @@ fn send_widgets(self: *Self, from: tp.pid_ref, m: tp.message) error{Exit}!bool {
fn send_mouse(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message) tp.result { fn send_mouse(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message) tp.result {
tp.trace(tp.channel.input, m); tp.trace(tp.channel.input, m);
_ = self.input_listeners_.send(from, m) catch {}; _ = self.input_listeners.send(from, m) catch {};
if (self.keyboard_focus) |w| { if (self.keyboard_focus) |w| {
_ = try w.send(from, m); _ = try w.send(from, m);
return; return;
@ -620,7 +580,7 @@ fn send_mouse(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message)
fn send_mouse_drag(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message) tp.result { fn send_mouse_drag(self: *Self, y: c_int, x: c_int, from: tp.pid_ref, m: tp.message) tp.result {
tp.trace(tp.channel.input, m); tp.trace(tp.channel.input, m);
_ = self.input_listeners_.send(from, m) catch {}; _ = self.input_listeners.send(from, m) catch {};
if (self.keyboard_focus) |w| { if (self.keyboard_focus) |w| {
_ = try w.send(from, m); _ = try w.send(from, m);
return; return;
@ -663,24 +623,23 @@ pub fn refresh_hover() void {
_ = self.update_hover(self.last_hover_y, self.last_hover_x) catch {}; _ = self.update_hover(self.last_hover_y, self.last_hover_x) catch {};
} }
pub fn save_config() (root.ConfigDirError || root.ConfigWriteError)!void { pub fn save_config() !void {
const self = current(); const self = current();
try root.write_config(self.config_, self.allocator); try root.write_config(self.config, self.allocator);
} }
pub fn is_mainview_focused() bool { pub fn is_mainview_focused() bool {
const self = current(); const self = current();
return self.mini_mode_ == null and self.input_mode_outer_ == null; return self.mini_mode == null and self.input_mode_outer == null;
} }
fn enter_overlay_mode(self: *Self, mode: type) command.Result { fn enter_overlay_mode(self: *Self, mode: type) command.Result {
command.executeName("disable_fast_scroll", .{}) catch {}; command.executeName("disable_fast_scroll", .{}) catch {};
command.executeName("disable_jump_mode", .{}) catch {}; command.executeName("disable_jump_mode", .{}) catch {};
if (self.mini_mode_) |_| try cmds.exit_mini_mode(self, .{}); if (self.mini_mode) |_| try cmds.exit_mini_mode(self, .{});
if (self.input_mode_outer_) |_| try cmds.exit_overlay_mode(self, .{}); if (self.input_mode_outer) |_| try cmds.exit_overlay_mode(self, .{});
self.input_mode_outer_ = self.input_mode_; self.input_mode_outer = self.input_mode;
self.input_mode_ = try mode.create(self.allocator); self.input_mode = try mode.create(self.allocator);
if (self.input_mode_) |*m| m.run_init();
refresh_hover(); refresh_hover();
} }
@ -689,49 +648,35 @@ fn get_input_mode(self: *Self, mode_name: []const u8) !Mode {
} }
fn enter_input_mode(self: *Self, new_mode: Mode) command.Result { fn enter_input_mode(self: *Self, new_mode: Mode) command.Result {
if (self.mini_mode_) |_| try cmds.exit_mini_mode(self, .{}); if (self.mini_mode) |_| try cmds.exit_mini_mode(self, .{});
if (self.input_mode_outer_) |_| try cmds.exit_overlay_mode(self, .{}); if (self.input_mode_outer) |_| try cmds.exit_overlay_mode(self, .{});
if (self.input_mode_) |*m| { if (self.input_mode) |*m| {
m.deinit(); m.deinit();
self.input_mode_ = null; self.input_mode = null;
} }
self.input_mode_ = new_mode; self.input_mode = new_mode;
if (self.input_mode_) |*m| m.run_init();
} }
fn refresh_input_mode(self: *Self) command.Result { fn refresh_input_mode(self: *Self) command.Result {
const mode = (self.input_mode_ orelse return).mode; const mode = (self.input_mode orelse return).mode;
var new_mode = self.get_input_mode(mode) catch ret: { var new_mode = self.get_input_mode(mode) catch ret: {
self.logger.print("unknown mode {s}", .{mode}); self.logger.print("unknown mode {s}", .{mode});
break :ret try self.get_input_mode(keybind.default_mode); break :ret try self.get_input_mode(keybind.default_mode);
}; };
errdefer new_mode.deinit(); errdefer new_mode.deinit();
if (self.input_mode_) |*m| { if (self.input_mode) |*m| {
m.deinit(); m.deinit();
self.input_mode_ = null; self.input_mode = null;
} }
self.input_mode_ = new_mode; self.input_mode = new_mode;
if (self.input_mode_) |*m| m.run_init();
}
fn set_theme_by_name(self: *Self, name: []const u8) !void {
const old = self.parsed_theme;
defer if (old) |p| p.deinit();
self.theme_, self.parsed_theme = get_theme_by_name(self.allocator, name) orelse {
self.logger.print("theme not found: {s}", .{name});
return;
};
self.config_.theme = self.theme_.name;
self.set_terminal_style();
self.logger.print("theme: {s}", .{self.theme_.description});
try save_config();
} }
pub const enter_mode_meta = .{ .arguments = &.{.string} };
const cmds = struct { const cmds = struct {
pub const Target = Self; pub const Target = Self;
const Ctx = command.Context; const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result; const Result = command.Result;
const Meta = command.Metadata;
pub fn restart(_: *Self, _: Ctx) Result { pub fn restart(_: *Self, _: Ctx) Result {
try tp.self_pid().send("restart"); try tp.self_pid().send("restart");
@ -743,54 +688,67 @@ const cmds = struct {
root.print_exit_status({}, "FORCE TERMINATE"); root.print_exit_status({}, "FORCE TERMINATE");
root.exit(99); root.exit(99);
} }
pub const force_terminate_meta: Meta = .{ .description = "Force quit without saving" }; pub const force_terminate_meta = .{ .description = "Force quit without saving" };
pub fn set_theme(self: *Self, ctx: Ctx) Result { pub fn set_theme(self: *Self, ctx: Ctx) Result {
var name: []const u8 = undefined; var name: []const u8 = undefined;
if (!try ctx.args.match(.{tp.extract(&name)})) if (!try ctx.args.match(.{tp.extract(&name)}))
return tp.exit_error(error.InvalidSetThemeArgument, null); return tp.exit_error(error.InvalidSetThemeArgument, null);
return self.set_theme_by_name(name); self.theme = get_theme_by_name(name) orelse {
self.logger.print("theme not found: {s}", .{name});
return;
};
self.config.theme = self.theme.name;
self.set_terminal_style();
self.logger.print("theme: {s}", .{self.theme.description});
try save_config();
} }
pub const set_theme_meta: Meta = .{ .arguments = &.{.string} }; pub const set_theme_meta = .{ .arguments = &.{.string} };
pub fn theme_next(self: *Self, _: Ctx) Result { pub fn theme_next(self: *Self, _: Ctx) Result {
const name = get_next_theme_by_name(self.theme_.name); self.theme = get_next_theme_by_name(self.theme.name);
return self.set_theme_by_name(name); self.config.theme = self.theme.name;
self.set_terminal_style();
self.logger.print("theme: {s}", .{self.theme.description});
try save_config();
} }
pub const theme_next_meta: Meta = .{ .description = "Next color theme" }; pub const theme_next_meta: Meta = .{ .description = "Next color theme" };
pub fn theme_prev(self: *Self, _: Ctx) Result { pub fn theme_prev(self: *Self, _: Ctx) Result {
const name = get_prev_theme_by_name(self.theme_.name); self.theme = get_prev_theme_by_name(self.theme.name);
return self.set_theme_by_name(name); self.config.theme = self.theme.name;
self.set_terminal_style();
self.logger.print("theme: {s}", .{self.theme.description});
try save_config();
} }
pub const theme_prev_meta: Meta = .{ .description = "Previous color theme" }; pub const theme_prev_meta: Meta = .{ .description = "Previous color theme" };
pub fn toggle_whitespace_mode(self: *Self, _: Ctx) Result { pub fn toggle_whitespace_mode(self: *Self, _: Ctx) Result {
self.config_.whitespace_mode = if (std.mem.eql(u8, self.config_.whitespace_mode, "none")) self.config.whitespace_mode = if (std.mem.eql(u8, self.config.whitespace_mode, "none"))
"indent" "indent"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "indent")) else if (std.mem.eql(u8, self.config.whitespace_mode, "indent"))
"leading" "leading"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "leading")) else if (std.mem.eql(u8, self.config.whitespace_mode, "leading"))
"eol" "eol"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "eol")) else if (std.mem.eql(u8, self.config.whitespace_mode, "eol"))
"tabs" "tabs"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "tabs")) else if (std.mem.eql(u8, self.config.whitespace_mode, "tabs"))
"visible" "visible"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "visible")) else if (std.mem.eql(u8, self.config.whitespace_mode, "visible"))
"full" "full"
else else
"none"; "none";
try save_config(); try save_config();
var buf: [32]u8 = undefined; var buf: [32]u8 = undefined;
const m = try tp.message.fmtbuf(&buf, .{ "whitespace_mode", self.config_.whitespace_mode }); const m = try tp.message.fmtbuf(&buf, .{ "whitespace_mode", self.config.whitespace_mode });
_ = try self.send_widgets(tp.self_pid(), m); _ = try self.send_widgets(tp.self_pid(), m);
self.logger.print("whitespace rendering {s}", .{self.config_.whitespace_mode}); self.logger.print("whitespace rendering {s}", .{self.config.whitespace_mode});
} }
pub const toggle_whitespace_mode_meta: Meta = .{ .description = "Next whitespace mode" }; pub const toggle_whitespace_mode_meta: Meta = .{ .description = "Next whitespace mode" };
pub fn toggle_input_mode(self: *Self, _: Ctx) Result { pub fn toggle_input_mode(self: *Self, _: Ctx) Result {
var it = std.mem.splitScalar(u8, self.config_.input_mode, '/'); var it = std.mem.splitScalar(u8, self.config.input_mode, '/');
self.config_.input_mode = it.first(); self.config.input_mode = it.first();
const namespaces = keybind.get_namespaces(self.allocator) catch |e| return tp.exit_error(e, @errorReturnTrace()); const namespaces = keybind.get_namespaces(self.allocator) catch |e| return tp.exit_error(e, @errorReturnTrace());
defer { defer {
@ -798,15 +756,15 @@ const cmds = struct {
self.allocator.free(namespaces); self.allocator.free(namespaces);
} }
var found = false; var found = false;
self.config_.input_mode = blk: for (namespaces) |namespace| { self.config.input_mode = blk: for (namespaces) |namespace| {
if (found) break :blk try self.allocator.dupe(u8, namespace); if (found) break :blk try self.allocator.dupe(u8, namespace);
if (std.mem.eql(u8, namespace, self.config_.input_mode)) if (std.mem.eql(u8, namespace, self.config.input_mode))
found = true; found = true;
} else try self.allocator.dupe(u8, namespaces[0]); } else try self.allocator.dupe(u8, namespaces[0]);
try save_config(); try save_config();
self.logger.print("input mode {s}", .{self.config_.input_mode}); self.logger.print("input mode {s}", .{self.config.input_mode});
try keybind.set_namespace(self.config_.input_mode); try keybind.set_namespace(self.config.input_mode);
return self.refresh_input_mode(); return self.refresh_input_mode();
} }
pub const toggle_input_mode_meta: Meta = .{ .description = "Switch input mode" }; pub const toggle_input_mode_meta: Meta = .{ .description = "Switch input mode" };
@ -828,12 +786,12 @@ const cmds = struct {
} }
return self.enter_input_mode(new_mode); return self.enter_input_mode(new_mode);
} }
pub const enter_mode_meta: Meta = .{ .arguments = &.{.string} }; pub const enter_mode_meta = .{ .arguments = &.{.string} };
pub fn enter_mode_default(self: *Self, _: Ctx) Result { pub fn enter_mode_default(self: *Self, _: Ctx) Result {
return enter_mode(self, Ctx.fmt(.{keybind.default_mode})); return enter_mode(self, Ctx.fmt(.{keybind.default_mode}));
} }
pub const enter_mode_default_meta: Meta = .{}; pub const enter_mode_default_meta = .{};
pub fn open_command_palette(self: *Self, _: Ctx) Result { pub fn open_command_palette(self: *Self, _: Ctx) Result {
return self.enter_overlay_mode(@import("mode/overlay/command_palette.zig").Type); return self.enter_overlay_mode(@import("mode/overlay/command_palette.zig").Type);
@ -863,7 +821,7 @@ const cmds = struct {
pub fn switch_buffers(self: *Self, _: Ctx) Result { pub fn switch_buffers(self: *Self, _: Ctx) Result {
return self.enter_overlay_mode(@import("mode/overlay/buffer_palette.zig").Type); return self.enter_overlay_mode(@import("mode/overlay/buffer_palette.zig").Type);
} }
pub const switch_buffers_meta: Meta = .{ .description = "Switch buffers" }; pub const switch_buffers_meta = .{ .description = "Switch buffers" };
pub fn select_task(self: *Self, _: Ctx) Result { pub fn select_task(self: *Self, _: Ctx) Result {
return self.enter_overlay_mode(@import("mode/overlay/task_palette.zig").Type); return self.enter_overlay_mode(@import("mode/overlay/task_palette.zig").Type);
@ -896,7 +854,7 @@ const cmds = struct {
return error.InvalidDeleteTaskArgument; return error.InvalidDeleteTaskArgument;
project_manager.delete_task(task) catch |e| return tp.exit_error(e, @errorReturnTrace()); project_manager.delete_task(task) catch |e| return tp.exit_error(e, @errorReturnTrace());
} }
pub const delete_task_meta: Meta = .{}; pub const delete_task_meta = .{};
pub fn change_theme(self: *Self, _: Ctx) Result { pub fn change_theme(self: *Self, _: Ctx) Result {
return self.enter_overlay_mode(@import("mode/overlay/theme_palette.zig").Type); return self.enter_overlay_mode(@import("mode/overlay/theme_palette.zig").Type);
@ -906,23 +864,23 @@ const cmds = struct {
pub fn change_file_type(self: *Self, _: Ctx) Result { pub fn change_file_type(self: *Self, _: Ctx) Result {
return self.enter_overlay_mode(@import("mode/overlay/file_type_palette.zig").Type); return self.enter_overlay_mode(@import("mode/overlay/file_type_palette.zig").Type);
} }
pub const change_file_type_meta: Meta = .{ .description = "Change file type" }; pub const change_file_type_meta = .{ .description = "Change file type" };
pub fn change_fontface(self: *Self, _: Ctx) Result { pub fn change_fontface(self: *Self, _: Ctx) Result {
if (build_options.gui) if (build_options.gui)
self.rdr_.get_fontfaces(); self.rdr.get_fontfaces();
} }
pub const change_fontface_meta: Meta = .{ .description = "Change font" }; pub const change_fontface_meta: Meta = .{ .description = "Change font" };
pub fn exit_overlay_mode(self: *Self, _: Ctx) Result { pub fn exit_overlay_mode(self: *Self, _: Ctx) Result {
self.rdr_.cursor_disable(); self.rdr.cursor_disable();
if (self.input_mode_outer_ == null) return enter_mode_default(self, .{}); if (self.input_mode_outer == null) return enter_mode_default(self, .{});
if (self.input_mode_) |*mode| mode.deinit(); if (self.input_mode) |*mode| mode.deinit();
self.input_mode_ = self.input_mode_outer_; self.input_mode = self.input_mode_outer;
self.input_mode_outer_ = null; self.input_mode_outer = null;
refresh_hover(); refresh_hover();
} }
pub const exit_overlay_mode_meta: Meta = .{}; pub const exit_overlay_mode_meta = .{};
pub fn find(self: *Self, ctx: Ctx) Result { pub fn find(self: *Self, ctx: Ctx) Result {
return enter_mini_mode(self, @import("mode/mini/find.zig"), ctx); return enter_mini_mode(self, @import("mode/mini/find.zig"), ctx);
@ -937,7 +895,7 @@ const cmds = struct {
pub fn goto(self: *Self, ctx: Ctx) Result { pub fn goto(self: *Self, ctx: Ctx) Result {
return enter_mini_mode(self, @import("mode/mini/goto.zig"), ctx); return enter_mini_mode(self, @import("mode/mini/goto.zig"), ctx);
} }
pub const goto_meta: Meta = .{ .description = "Goto line" }; pub const goto_meta = .{ .description = "Goto line" };
pub fn move_to_char(self: *Self, ctx: Ctx) Result { pub fn move_to_char(self: *Self, ctx: Ctx) Result {
return enter_mini_mode(self, @import("mode/mini/move_to_char.zig"), ctx); return enter_mini_mode(self, @import("mode/mini/move_to_char.zig"), ctx);
@ -956,51 +914,43 @@ const cmds = struct {
} }
return enter_mini_mode(self, @import("mode/mini/open_file.zig"), ctx); return enter_mini_mode(self, @import("mode/mini/open_file.zig"), ctx);
} }
pub const open_file_meta: Meta = .{ .description = "Open file" }; pub const open_file_meta = .{ .description = "Open file" };
pub fn save_as(self: *Self, ctx: Ctx) Result { pub fn save_as(self: *Self, ctx: Ctx) Result {
return enter_mini_mode(self, @import("mode/mini/save_as.zig"), ctx); return enter_mini_mode(self, @import("mode/mini/save_as.zig"), ctx);
} }
pub const save_as_meta: Meta = .{ .description = "Save as" }; pub const save_as_meta = .{ .description = "Save as" };
fn enter_mini_mode(self: *Self, comptime mode: anytype, ctx: Ctx) !void { fn enter_mini_mode(self: *Self, comptime mode: anytype, ctx: Ctx) !void {
command.executeName("disable_fast_scroll", .{}) catch {}; command.executeName("disable_fast_scroll", .{}) catch {};
command.executeName("disable_jump_mode", .{}) catch {}; command.executeName("disable_jump_mode", .{}) catch {};
const input_mode_, const mini_mode_ = try mode.create(self.allocator, ctx); const input_mode_, const mini_mode_ = try mode.create(self.allocator, ctx);
if (self.mini_mode_) |_| try exit_mini_mode(self, .{}); if (self.mini_mode) |_| try exit_mini_mode(self, .{});
if (self.input_mode_outer_) |_| try exit_overlay_mode(self, .{}); if (self.input_mode_outer) |_| try exit_overlay_mode(self, .{});
if (self.input_mode_outer_ != null) @panic("exit_overlay_mode failed"); if (self.input_mode_outer != null) @panic("exit_overlay_mode failed");
self.input_mode_outer_ = self.input_mode_; self.input_mode_outer = self.input_mode;
self.input_mode_ = input_mode_; self.input_mode = input_mode_;
self.mini_mode_ = mini_mode_; self.mini_mode = mini_mode_;
if (self.input_mode_) |*m| m.run_init();
} }
pub fn exit_mini_mode(self: *Self, _: Ctx) Result { pub fn exit_mini_mode(self: *Self, _: Ctx) Result {
self.rdr_.cursor_disable(); self.rdr.cursor_disable();
if (self.mini_mode_) |_| {} else return; if (self.mini_mode) |_| {} else return;
if (self.input_mode_) |*mode| mode.deinit(); if (self.input_mode) |*mode| mode.deinit();
self.input_mode_ = self.input_mode_outer_; self.input_mode = self.input_mode_outer;
self.input_mode_outer_ = null; self.input_mode_outer = null;
self.mini_mode_ = null; self.mini_mode = null;
} }
pub const exit_mini_mode_meta: Meta = .{}; pub const exit_mini_mode_meta = .{};
pub fn open_keybind_config(self: *Self, _: Ctx) Result { pub fn open_keybind_config(self: *Self, _: Ctx) Result {
var mode_parts = std.mem.splitScalar(u8, self.config_.input_mode, '/'); var mode_parts = std.mem.splitScalar(u8, self.config.input_mode, '/');
const namespace_name = mode_parts.first(); const namespace_name = mode_parts.first();
const file_name = try keybind.get_or_create_namespace_config_file(self.allocator, namespace_name); const file_name = try keybind.get_or_create_namespace_config_file(self.allocator, namespace_name);
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } }); try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
self.logger.print("restart flow to use changed key bindings", .{}); self.logger.print("restart flow to use changed key bindings", .{});
} }
pub const open_keybind_config_meta: Meta = .{ .description = "Edit key bindings" }; pub const open_keybind_config_meta = .{ .description = "Edit key bindings" };
pub fn open_custom_theme(self: *Self, _: Ctx) Result {
const file_name = try self.get_or_create_theme_file(self.allocator);
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
self.logger.print("restart flow to use changed theme", .{});
}
pub const open_custom_theme_meta: Meta = .{ .description = "Customize theme" };
pub fn run_async(self: *Self, ctx: Ctx) Result { pub fn run_async(self: *Self, ctx: Ctx) Result {
var iter = ctx.args.buf; var iter = ctx.args.buf;
@ -1041,27 +991,27 @@ const cmds = struct {
} }
try tp.self_pid().send_raw(.{ .buf = msg_cb.items }); try tp.self_pid().send_raw(.{ .buf = msg_cb.items });
} }
pub const run_async_meta: Meta = .{}; pub const run_async_meta = .{};
pub fn enter_vim_mode(_: *Self, _: Ctx) Result { pub fn enter_vim_mode(_: *Self, _: Ctx) Result {
try @import("mode/vim.zig").init(); try @import("mode/vim.zig").init();
} }
pub const enter_vim_mode_meta: Meta = .{}; pub const enter_vim_mode_meta = .{};
pub fn exit_vim_mode(_: *Self, _: Ctx) Result { pub fn exit_vim_mode(_: *Self, _: Ctx) Result {
@import("mode/vim.zig").deinit(); @import("mode/vim.zig").deinit();
} }
pub const exit_vim_mode_meta: Meta = .{}; pub const exit_vim_mode_meta = .{};
pub fn enter_helix_mode(_: *Self, _: Ctx) Result { pub fn enter_helix_mode(_: *Self, _: Ctx) Result {
try @import("mode/helix.zig").init(); try @import("mode/helix.zig").init();
} }
pub const enter_helix_mode_meta: Meta = .{}; pub const enter_helix_mode_meta = .{};
pub fn exit_helix_mode(_: *Self, _: Ctx) Result { pub fn exit_helix_mode(_: *Self, _: Ctx) Result {
@import("mode/helix.zig").deinit(); @import("mode/helix.zig").deinit();
} }
pub const exit_helix_mode_meta: Meta = .{}; pub const exit_helix_mode_meta = .{};
}; };
pub const MiniMode = struct { pub const MiniMode = struct {
@ -1080,51 +1030,43 @@ fn current() *Self {
} }
pub fn rdr() *renderer { pub fn rdr() *renderer {
return &current().rdr_; return &current().rdr;
} }
pub fn message_filters() *MessageFilter.List { pub fn message_filters() *MessageFilter.List {
return &current().message_filters_; return &current().message_filters;
} }
pub fn input_listeners() *EventHandler.List { pub fn input_listeners() *EventHandler.List {
return &current().input_listeners_; return &current().input_listeners;
} }
pub fn input_mode() ?*Mode { pub fn input_mode() ?*Mode {
return if (current().input_mode_) |*p| p else null; return if (current().input_mode) |*p| p else null;
} }
pub fn input_mode_outer() ?*Mode { pub fn input_mode_outer() ?*Mode {
return if (current().input_mode_outer_) |*p| p else null; return if (current().input_mode_outer) |*p| p else null;
} }
pub fn mini_mode() ?*MiniMode { pub fn mini_mode() ?*MiniMode {
return if (current().mini_mode_) |*p| p else null; return if (current().mini_mode) |*p| p else null;
}
pub fn query_cache() *syntax.QueryCache {
return current().query_cache_;
} }
pub fn config() *const @import("config") { pub fn config() *const @import("config") {
return &current().config_; return &current().config;
}
pub fn highlight_columns() []const u16 {
return current().highlight_columns_;
} }
pub fn config_mut() *@import("config") { pub fn config_mut() *@import("config") {
return &current().config_; return &current().config;
} }
pub fn mainview() ?*MainView { pub fn mainview() ?*MainView {
return if (current().mainview_) |*mv| mv.dynamic_cast(MainView) else null; return if (current().mainview) |*mv| mv.dynamic_cast(MainView) else null;
} }
pub fn mainview_widget() Widget { pub fn mainview_widget() Widget {
return current().mainview_ orelse @panic("tui main view not found"); return current().mainview orelse @panic("tui main view not found");
} }
pub fn get_active_editor() ?*@import("editor.zig").Editor { pub fn get_active_editor() ?*@import("editor.zig").Editor {
@ -1148,9 +1090,9 @@ fn context_check() void {
} }
pub fn get_mode() []const u8 { pub fn get_mode() []const u8 {
return if (current().mini_mode_) |m| return if (current().mini_mode) |m|
m.name m.name
else if (current().input_mode_) |m| else if (current().input_mode) |m|
m.name m.name
else else
"INI"; "INI";
@ -1158,7 +1100,7 @@ pub fn get_mode() []const u8 {
pub fn get_keybind_mode() ?Mode { pub fn get_keybind_mode() ?Mode {
const self = current(); const self = current();
return self.input_mode_ orelse self.delayed_init_input_mode; return self.input_mode orelse self.delayed_init_input_mode;
} }
pub fn reset_drag_context() void { pub fn reset_drag_context() void {
@ -1181,11 +1123,11 @@ pub fn resize() void {
} }
pub fn plane() renderer.Plane { pub fn plane() renderer.Plane {
return current().rdr_.stdplane(); return current().rdr.stdplane();
} }
fn stdplane(self: *Self) renderer.Plane { fn stdplane(self: *Self) renderer.Plane {
return self.rdr_.stdplane(); return self.rdr.stdplane();
} }
pub fn egc_chunk_width(chunk: []const u8, abs_col: usize, tab_width: usize) usize { pub fn egc_chunk_width(chunk: []const u8, abs_col: usize, tab_width: usize) usize {
@ -1201,49 +1143,44 @@ pub fn screen() Widget.Box {
} }
pub fn fontface() []const u8 { pub fn fontface() []const u8 {
return current().fontface_; return current().fontface;
} }
pub fn fontfaces(allocator: std.mem.Allocator) error{OutOfMemory}![][]const u8 { pub fn fontfaces(allocator: std.mem.Allocator) error{OutOfMemory}![][]const u8 {
return current().fontfaces_.toOwnedSlice(allocator); return current().fontfaces.toOwnedSlice(allocator);
} }
pub fn theme() *const Widget.Theme { pub fn theme() *const Widget.Theme {
return &current().theme_; return &current().theme;
} }
pub fn get_theme_by_name(allocator: std.mem.Allocator, name: []const u8) ?struct { Widget.Theme, ?std.json.Parsed(Widget.Theme) } { pub fn get_theme_by_name(name: []const u8) ?Widget.Theme {
if (load_theme_file(allocator, name) catch null) |parsed_theme| {
std.log.info("loaded theme from file: {s}", .{name});
return .{ parsed_theme.value, parsed_theme };
}
for (Widget.themes) |theme_| { for (Widget.themes) |theme_| {
if (std.mem.eql(u8, theme_.name, name)) if (std.mem.eql(u8, theme_.name, name))
return .{ theme_, null }; return theme_;
} }
return null; return null;
} }
fn get_next_theme_by_name(name: []const u8) []const u8 { pub fn get_next_theme_by_name(name: []const u8) Widget.Theme {
var next = false; var next = false;
for (Widget.themes) |theme_| { for (Widget.themes) |theme_| {
if (next) if (next)
return theme_.name; return theme_;
if (std.mem.eql(u8, theme_.name, name)) if (std.mem.eql(u8, theme_.name, name))
next = true; next = true;
} }
return Widget.themes[0].name; return Widget.themes[0];
} }
fn get_prev_theme_by_name(name: []const u8) []const u8 { pub fn get_prev_theme_by_name(name: []const u8) Widget.Theme {
var prev: ?Widget.Theme = null; var prev: ?Widget.Theme = null;
for (Widget.themes) |theme_| { for (Widget.themes) |theme_| {
if (std.mem.eql(u8, theme_.name, name)) if (std.mem.eql(u8, theme_.name, name))
return (prev orelse Widget.themes[Widget.themes.len - 1]).name; return prev orelse Widget.themes[Widget.themes.len - 1];
prev = theme_; prev = theme_;
} }
return Widget.themes[Widget.themes.len - 1].name; return Widget.themes[Widget.themes.len - 1];
} }
pub fn find_scope_style(theme_: *const Widget.Theme, scope: []const u8) ?Widget.Theme.Token { pub fn find_scope_style(theme_: *const Widget.Theme, scope: []const u8) ?Widget.Theme.Token {
@ -1332,15 +1269,15 @@ pub const fallbacks: []const FallBack = &[_]FallBack{
}; };
fn set_terminal_style(self: *Self) void { fn set_terminal_style(self: *Self) void {
if (build_options.gui or self.config_.enable_terminal_color_scheme) { if (build_options.gui or self.config.enable_terminal_color_scheme) {
self.rdr_.set_terminal_style(self.theme_.editor); self.rdr.set_terminal_style(self.theme.editor);
self.rdr_.set_terminal_cursor_color(self.theme_.editor_cursor.bg.?); self.rdr.set_terminal_cursor_color(self.theme.editor_cursor.bg.?);
} }
} }
pub fn get_cursor_shape() renderer.CursorShape { pub fn get_cursor_shape() renderer.CursorShape {
const self = current(); const self = current();
const shape = if (self.input_mode_) |mode| mode.cursor_shape orelse self.default_cursor else self.default_cursor; const shape = if (self.input_mode) |mode| mode.cursor_shape orelse self.default_cursor else self.default_cursor;
return switch (shape) { return switch (shape) {
.default => .default, .default => .default,
.block_blink => .block_blink, .block_blink => .block_blink,
@ -1360,7 +1297,7 @@ pub fn is_cursor_beam() bool {
} }
pub fn get_selection_style() @import("Buffer").Selection.Style { pub fn get_selection_style() @import("Buffer").Selection.Style {
return if (current().input_mode_) |mode| mode.selection_style else .normal; return if (current().input_mode) |mode| mode.selection_style else .normal;
} }
pub fn message(comptime fmt: anytype, args: anytype) void { pub fn message(comptime fmt: anytype, args: anytype) void {
@ -1386,32 +1323,3 @@ pub fn render_match_cell(self: *renderer.Plane, y: usize, x: usize, theme_: *con
cell.set_style(theme_.editor_match); cell.set_style(theme_.editor_match);
_ = self.putc(&cell) catch {}; _ = self.putc(&cell) catch {};
} }
fn get_or_create_theme_file(self: *Self, allocator: std.mem.Allocator) ![]const u8 {
const theme_name = self.theme_.name;
if (root.read_theme(allocator, theme_name)) |content| {
allocator.free(content);
} else {
var buf = std.ArrayList(u8).init(self.allocator);
defer buf.deinit();
try std.json.stringify(self.theme_, .{ .whitespace = .indent_2 }, buf.writer());
try root.write_theme(
theme_name,
buf.items,
);
}
return try root.get_theme_file_name(theme_name);
}
fn load_theme_file(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Widget.Theme) {
return load_theme_file_internal(allocator, theme_name) catch |e| {
std.log.err("loaded theme from file failed: {}", .{e});
return e;
};
}
fn load_theme_file_internal(allocator: std.mem.Allocator, theme_name: []const u8) !?std.json.Parsed(Widget.Theme) {
_ = std.json.Scanner;
const json_str = root.read_theme(allocator, theme_name) orelse return null;
defer allocator.free(json_str);
return try std.json.parseFromSlice(Widget.Theme, allocator, json_str, .{ .allocate = .alloc_always });
}

View file

@ -41,12 +41,12 @@ pub const Font = struct {
); );
if (hr < 0) std.debug.panic( if (hr < 0) std.debug.panic(
"CreateTextFormat '{}' height {d} failed, hresult=0x{x}", "CreateTextFormat '{}' height {d} failed, hresult=0x{x}",
.{ std.unicode.fmtUtf16Le(face.slice()), size, @as(u32, @bitCast(hr)) }, .{ std.unicode.fmtUtf16le(face.slice()), size, @as(u32, @bitCast(hr)) },
); );
} }
errdefer _ = text_format.IUnknown.Release(); errdefer _ = text_format.IUnknown.Release();
const cell_size: XY(u16) = blk: { const cell_size = blk: {
var text_layout: *win32.IDWriteTextLayout = undefined; var text_layout: *win32.IDWriteTextLayout = undefined;
{ {
const hr = global.dwrite_factory.CreateTextLayout( const hr = global.dwrite_factory.CreateTextLayout(

View file

@ -143,7 +143,7 @@ fn getConfig() *gui_config {
} }
fn getFieldDefault(field: std.builtin.Type.StructField) ?*const field.type { fn getFieldDefault(field: std.builtin.Type.StructField) ?*const field.type {
return @alignCast(@ptrCast(field.default_value_ptr orelse return null)); return @alignCast(@ptrCast(field.default_value orelse return null));
} }
fn getDefaultFontFace() FontFace { fn getDefaultFontFace() FontFace {