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
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
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)
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.
[![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.
Flow builds with zig 0.14.0 at this time. Build with:
Flow builds with zig 0.13 at this time. Build with:
```shell
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 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)(
b,
run_step,
@ -36,7 +29,6 @@ pub fn build(b: *std.Build) void {
use_llvm,
pie,
gui,
version.items,
);
}
@ -52,7 +44,6 @@ fn build_development(
use_llvm: ?bool,
pie: ?bool,
gui: bool,
version: []const u8,
) void {
const target = b.standardTargetOptions(.{ .default_target = .{ .abi = if (builtin.os.tag == .linux and !tracy_enabled) .musl else null } });
const optimize = b.standardOptimizeOption(.{});
@ -72,7 +63,6 @@ fn build_development(
use_llvm,
pie,
gui,
version,
);
}
@ -88,7 +78,6 @@ fn build_release(
use_llvm: ?bool,
pie: ?bool,
_: bool, //gui
version: []const u8,
) void {
const targets: []const std.Target.Query = &.{
.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .musl },
@ -102,8 +91,11 @@ fn build_release(
};
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 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);
for (targets) |t| {
@ -128,7 +120,6 @@ fn build_release(
use_llvm,
pie,
false, //gui
version,
);
if (t.os_tag == .windows)
@ -147,7 +138,6 @@ fn build_release(
use_llvm,
pie,
true, //gui
version,
);
}
}
@ -167,7 +157,6 @@ pub fn build_exe(
use_llvm: ?bool,
pie: ?bool,
gui: bool,
version: []const u8,
) void {
const options = b.addOptions();
options.addOption(bool, "enable_tracy", tracy_enabled);
@ -194,8 +183,7 @@ pub fn build_exe(
};
const wf = b.addWriteFiles();
const version_file = wf.add("version", version);
const version_info_file = wf.add("version_info", version_info.items);
const version_info_file = wf.add("version", version_info.items);
const vaxis_dep = b.dependency("vaxis", .{
.target = target,
@ -225,12 +213,7 @@ pub fn build_exe(
});
const thespian_mod = thespian_dep.module("thespian");
const cbor_dep = thespian_dep.builder.dependency("cbor", .{
.target = target,
.optimize = optimize_deps,
});
const cbor_mod = cbor_dep.module("cbor");
const cbor_mod = thespian_dep.module("cbor");
const tracy_dep = if (tracy_enabled) thespian_dep.builder.dependency("tracy", .{
.target = target,
@ -305,10 +288,6 @@ pub fn build_exe(
.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(.{
.root_source_file = b.path("src/buffer/Buffer.zig"),
.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(.{
.root_source_file = b.path("src/ripgrep.zig"),
.imports = &.{
@ -461,7 +430,6 @@ pub fn build_exe(
.{ .name = "syntax", .module = syntax_mod },
.{ .name = "dizzy", .module = dizzy_dep.module("dizzy") },
.{ .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 = "keybind", .module = keybind_mod },
.{ .name = "shell", .module = shell_mod },
.{ .name = "git", .module = git_mod },
.{ .name = "ripgrep", .module = ripgrep_mod },
.{ .name = "theme", .module = themes_dep.module("theme") },
.{ .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("syntax", syntax_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 }));
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("syntax", syntax_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_step.dependOn(&check_exe.step);
@ -620,7 +583,7 @@ pub fn build_exe(
});
lint_step.dependOn(&lints.step);
b.default_step.dependOn(lint_step);
// b.default_step.dependOn(lint_step);
}
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",
.minimum_zig_version = "0.14.0",
.fingerprint = 0x52c0d670590aa80f,
.minimum_zig_version = "0.13.0",
.dependencies = .{
.syntax = .{ .path = "src/syntax" },
.flags = .{
.url = "https://github.com/n0s4/flags/archive/372501d1576b5723829bcba98e41361132c7b618.tar.gz",
.hash = "flags-0.8.0-AAAAAJV0AACuGBBnpUnHqZzAhoGTp4ibFROBQQQZGRqx",
.url = "https://github.com/n0s4/flags/archive/b3905aa990719ff567f1c5a2f89e6dd3292d8533.tar.gz",
.hash = "1220930a42f8da3fb7f723e3ad3f6dcc6db76327dd8d26274566423192d53e91b2bb",
},
.dizzy = .{
.url = "https://github.com/neurocyte/dizzy/archive/455d18369cbb2a0458ba70be919cd378338d695e.tar.gz",
.hash = "dizzy-1.0.0-AAAAAM1wAAAiDbx_6RwcVEOBk8p2XOu8t9WPNc3K7kBK",
.hash = "1220220dbc7fe91c1c54438193ca765cebbcb7d58f35cdcaee404a9d2245a42a4362",
},
.thespian = .{
.url = "https://github.com/neurocyte/thespian/archive/42a98a44e7590fd6e952b75523e5c0f4fa0e6c97.tar.gz",
.hash = "thespian-0.0.1-owFOjnUTBgBUlBtQ-SbN_6S7bXE6e2mKmCgk4A-RGBMA",
.url = "https://github.com/neurocyte/thespian/archive/9ca04ddfc715e0f7d29d3f6b39269ad9bf174230.tar.gz",
.hash = "1220b05b5949454bf155a802d5034c060431b8bf59f9d4d2d5241397e9fd201d78d9",
},
.themes = .{
.url = "https://github.com/neurocyte/flow-themes/releases/download/master-ac2e3fe2df3419b71276f86fa9c45fd39d668f23/flow-themes.tar.gz",
.hash = "N-V-__8AAEtaFwAjAHCmWHRCrBxL7uSG4hQiIsSgS32Y67K6",
.url = "https://github.com/neurocyte/flow-themes/releases/download/master-59bf204551bcb238faddd06779063570e7e6d431/flow-themes.tar.gz",
.hash = "12209a213a392ea80ea5c7dde125e7161bb0278ac4b99db9df2b7af783710bcb09f7",
},
.fuzzig = .{
.url = "https://github.com/fjebaker/fuzzig/archive/44c04733c7c0fee3db83672aaaaf4ed03e943156.tar.gz",
.hash = "fuzzig-0.1.1-AAAAALNIAQBmbHr-MPalGuR393Vem2pTQXI7_LXeNJgX",
.url = "https://github.com/fjebaker/fuzzig/archive/0fd156d5097365151e85a85eef9d8cf0eebe7b00.tar.gz",
.hash = "122019f077d09686b1ec47928ca2b4bf264422f3a27afc5b49dafb0129a4ceca0d01",
},
.vaxis = .{
.url = "https://github.com/neurocyte/libvaxis/archive/386e554f275c82ebfde7f15765687aaa6e89f6f6.tar.gz",
.hash = "vaxis-0.1.0-BWNV_FneCADwINyXfMcWHjv_vpQR-VOzrGSmH6BEP40y",
.url = "https://github.com/neurocyte/libvaxis/archive/e518e139417a9773f59624961b02e05b8fffff35.tar.gz",
.hash = "122045fec2cedf3f68c20f961c42f3ca1e901238aec5a0c7b3b632cd21ce3b621f76",
},
.zeit = .{
.url = "https://github.com/rockorager/zeit/archive/8fd203f85f597f16e0a525c1f1ca1e0bffded809.tar.gz",
.hash = "zeit-0.0.0-AAAAACVbAgAiIzg1rccZU1qOfO_dKQKme7-37xmEQcqc",
.hash = "122022233835adc719535a8e7cefdd2902a67bbfb7ef198441ca9ce89c0593f488c2",
},
.win32 = .{
.url = "https://github.com/marlersoft/zigwin32/archive/e8739b32a33ce48a3286aba31918b26a9dfc6ef0.tar.gz",
.hash = "zigwin32-25.0.28-preview-AAAAAEEl_AMhnKSs-lgEyjmUX5JVTpNQewd8A2Bbekwc",
.url = "https://github.com/marlersoft/zigwin32/archive/259b6f353a48968d7e3171573db4fd898b046188.tar.gz",
.hash = "1220925614447b54ccc9894bbba8b202c6a8b750267890edab7732064867e46f3217",
.lazy = true,
},
},

View file

@ -16,7 +16,7 @@ pub const VTable = struct {
pub fn to_owned(pimpl: anytype) Self {
const impl = @typeInfo(@TypeOf(pimpl));
const child: type = impl.pointer.child;
const child: type = impl.Pointer.child;
return .{
.ptr = pimpl,
.vtable = comptime &.{
@ -56,7 +56,7 @@ var none = {};
pub fn to_unowned(pimpl: anytype) Self {
const impl = @typeInfo(@TypeOf(pimpl));
const child: type = impl.pointer.child;
const child: type = impl.Pointer.child;
return .{
.ptr = pimpl,
.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 {
const impl = @typeInfo(@TypeOf(pimpl));
const child: type = impl.pointer.child;
const child: type = impl.Pointer.child;
return .{
.ptr = pimpl,
.vtable = comptime &.{

View file

@ -15,7 +15,7 @@ const debug_lsp = true;
const OutOfMemoryError = error{OutOfMemory};
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 {
return .{ .allocator = allocator, .pid = try Process.create(allocator, project, cmd) };
@ -31,17 +31,12 @@ pub fn term(self: Self) void {
self.pid.deinit();
}
pub fn send_request(
self: Self,
allocator: std.mem.Allocator,
method: []const u8,
m: anytype,
ctx: anytype,
) (OutOfMemoryError || SpawnError)!void {
pub fn send_request(self: Self, allocator: std.mem.Allocator, method: []const u8, m: anytype) CallError!tp.message {
var cb = std.ArrayList(u8).init(self.allocator);
defer cb.deinit();
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 {
@ -59,57 +54,6 @@ pub fn close(self: *Self) void {
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 {
allocator: std.mem.Allocator,
cmd: tp.message,
@ -123,10 +67,6 @@ const Process = struct {
log_file: ?std.fs.File = null,
next_id: i32 = 0,
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);
@ -163,7 +103,6 @@ const Process = struct {
}
fn deinit(self: *Process) void {
self.free_init_queue();
var i = self.requests.iterator();
while (i.next()) |req| {
self.allocator.free(req.key_ptr.*);
@ -227,37 +166,24 @@ const Process = struct {
UnsupportedType,
ExitNormal,
ExitUnexpected,
InvalidMapType,
});
fn receive_safe(self: *Process, from: tp.pid_ref, m: tp.message) Error!void {
const frame = tracy.initZone(@src(), .{ .name = module_name });
defer frame.deinit();
errdefer self.deinit();
var method: []const u8 = "";
var bytes: []const u8 = "";
var err: []const u8 = "";
var method: []u8 = "";
var bytes: []u8 = "";
var err: []u8 = "";
var code: u32 = 0;
var cbor_id: []const u8 = "";
if (try cbor.match(m.buf, .{ "REQ", "initialize", tp.extract(&bytes) })) {
try self.send_request(from, "initialize", 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),
}
if (try cbor.match(m.buf, .{ "REQ", tp.extract(&method), tp.extract(&bytes) })) {
try self.send_request(from, method, 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);
} 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) })) {
switch (self.state) {
.init => try self.append_init_queue(from, method, bytes, .notify), //queue requests
.running => try self.send_notification(method, bytes),
}
try self.send_notification(method, bytes);
} else if (try cbor.match(m.buf, .{"close"})) {
self.write_log("### LSP close ###\n", .{});
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 {
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);
self.write_log("### RECV:\n{s}\n###\n", .{bytes});
self.frame_message_recv() catch |e| {

View file

@ -7,7 +7,6 @@ const dizzy = @import("dizzy");
const Buffer = @import("Buffer");
const fuzzig = @import("fuzzig");
const tracy = @import("tracy");
const git = @import("git");
const builtin = @import("builtin");
const LSP = @import("LSP.zig");
@ -22,17 +21,13 @@ language_servers: std.StringHashMap(LSP),
file_language_server: std.StringHashMap(LSP),
tasks: std.ArrayList(Task),
persistent: bool = false,
logger_lsp: log.Logger,
logger_git: log.Logger,
workspace: ?[]const u8 = null,
branch: ?[]const u8 = null,
const Self = @This();
const OutOfMemoryError = error{OutOfMemory};
const CallError = tp.CallError;
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 LspError = (error{ NoLsp, LspFailed } || 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),
.file_language_server = std.StringHashMap(LSP).init(allocator),
.tasks = std.ArrayList(Task).init(allocator),
.logger_lsp = log.logger("lsp"),
.logger_git = log.logger("git"),
};
}
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();
while (i_.next()) |p| {
self.allocator.free(p.key_ptr.*);
@ -86,8 +77,6 @@ pub fn deinit(self: *Self) void {
self.files.deinit();
for (self.tasks.items) |task| self.allocator.free(task.command);
self.tasks.deinit();
self.logger_lsp.deinit();
self.logger_git.deinit();
self.allocator.free(self.name);
}
@ -143,7 +132,7 @@ pub fn restore_state(self: *Self, data: []const u8) !void {
var iter: []const u8 = data;
_ = cbor.matchValue(&iter, tp.string) catch {};
_ = 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 }),
};
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{
OutOfMemory,
IntegerTooLarge,
IntegerTooSmall,
InvalidType,
TooShort,
InvalidFloatType,
InvalidArrayType,
InvalidPIntType,
JsonIncompatibleType,
NotAnObject,
}!void {
pub fn restore_state_v0(self: *Self, data: []const u8) error{ OutOfMemory, IntegerTooLarge, IntegerTooSmall, InvalidType, TooShort }!void {
tp.trace(tp.channel.debug, .{"restore_state_v0"});
defer self.sort_files_by_mtime();
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);
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;
try self.send_lsp_init_request(lsp, self.name, basename, uri, language_server);
const response = try self.send_lsp_init_request(lsp, self.name, basename, uri);
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);
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 uri = try self.make_URI(file_path);
defer self.allocator.free(uri);
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, .{
const response = lsp.send_request(self.allocator, method, .{
.textDocument = .{ .uri = uri },
.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 targetUri: ?[]const u8 = 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 uri = try self.make_URI(file_path);
defer self.allocator.free(uri);
self.logger_lsp.print("finding references...", .{});
log.logger("lsp").print("finding references...", .{});
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 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", .{
const response = lsp.send_request(self.allocator, "textDocument/references", .{
.textDocument = .{ .uri = uri },
.position = .{ .line = row, .character = col },
.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 {};
var iter = locations;
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) {
var location: []const u8 = undefined;
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;
}
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 {
const allocator = std.heap.c_allocator;
fn send_reference(self: *Self, to: tp.pid_ref, location: []const u8) (ClientError || InvalidMessageError || GetLineOfFileError || cbor.Error)!void {
var iter = location;
var targetUri: ?[]const u8 = 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] = '\\';
};
}
const line = try get_line_of_file(allocator, file_path, targetRange.?.start.line);
defer allocator.free(line);
const file_path_ = if (file_path.len > name.len and std.mem.eql(u8, name, file_path[0..name.len]))
file_path[name.len + 1 ..]
const line = try self.get_line_of_file(self.allocator, file_path, targetRange.?.start.line);
defer self.allocator.free(line);
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[self.name.len + 1 ..]
else
file_path;
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 uri = try self.make_URI(file_path);
defer self.allocator.free(uri);
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", .{
const response = lsp.send_request(self.allocator, "textDocument/completion", .{
.textDocument = .{ .uri = uri },
.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 len = cbor.decodeMapHeader(&iter) catch return;
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);
}
}
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 len = cbor.decodeArrayHeader(&iter) catch return;
var item: []const u8 = "";
while (len > 0) : (len -= 1) {
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_detail: []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 uri = try self.make_URI(file_path);
defer self.allocator.free(uri);
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", .{
const response = lsp.send_request(self.allocator, "textDocument/rename", .{
.textDocument = .{ .uri = uri },
.position = .{ .line = row, .character = col },
.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": []}
// 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 len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage;
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 (std.mem.eql(u8, field_name, "changes")) {
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;
} else if (std.mem.eql(u8, field_name, "documentChanges")) {
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;
} else {
try cbor.skipValue(&iter);
@ -1112,17 +1020,17 @@ fn decode_rename_symbol_map(result: []const u8, renames: *std.ArrayList(Rename))
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 files_len = cbor.decodeMapHeader(&iter) catch return error.InvalidMessage;
while (files_len > 0) : (files_len -= 1) {
var file_uri: []const u8 = undefined;
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 changes_len = cbor.decodeArrayHeader(&iter) catch return error.InvalidMessage;
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")) {
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
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;
while (text_edits_len > 0) : (text_edits_len -= 1) {
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 uri = try self.make_URI(file_path);
defer self.allocator.free(uri);
// self.logger_lsp.print("fetching hover information...", .{});
// log.logger("lsp").print("fetching hover information...", .{});
const handler: struct {
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", .{
const response = lsp.send_request(self.allocator, "textDocument/hover", .{
.textDocument = .{ .uri = uri },
.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 len = cbor.decodeMapHeader(&iter) catch return;
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)
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(
self: *Self,
to: tp.pid_ref,
tag: []const u8,
file_path: []const u8,
@ -1257,7 +1147,7 @@ fn send_contents(
};
if (is_list) {
var content = std.ArrayList(u8).init(std.heap.c_allocator);
var content = std.ArrayList(u8).init(self.allocator);
defer content.deinit();
while (len > 0) : (len -= 1) {
if (try cbor.matchValue(&iter, cbor.extract(&value))) {
@ -1428,7 +1318,7 @@ fn read_position(position: []const u8) !Position {
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 message: ?[]const u8 = null;
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 logger = log.logger("lsp");
defer logger.deinit();
if (type_ <= 2)
self.logger_lsp.err_msg("lsp", msg)
logger.err_msg("lsp", msg)
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 {
@ -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;
}
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 {
const handler: struct {
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", .{
fn send_lsp_init_request(self: *Self, lsp: LSP, project_path: []const u8, project_basename: []const u8, project_uri: []const u8) CallError!tp.message {
return lsp.send_request(self.allocator, "initialize", .{
.processId = if (builtin.os.tag == .linux) std.os.linux.getpid() else null,
.rootPath = project_path,
.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) {
@ -1830,7 +1698,7 @@ const eol = '\n';
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 file = try std.fs.cwd().openFile(file_path, .{ .mode = .read_only });
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;
for (0..buf.len) |i| {
if (line_count == line)
return get_line(allocator, buf[i..]);
return self.get_line(allocator, buf[i..]);
if (buf[i] == eol) line_count += 1;
}
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| {
if (buf[i] == eol) return allocator.dupe(u8, buf[0..i]);
}
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,
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 {
root: Root,
@ -71,26 +71,26 @@ const UndoBranch = struct {
};
pub const WalkerMut = struct {
keep_walking_: bool = false,
found_: bool = false,
keep_walking: bool = false,
found: bool = false,
replace: ?Root = null,
err: ?anyerror = null,
pub const keep_walking = WalkerMut{ .keep_walking_ = true };
pub const stop = WalkerMut{ .keep_walking_ = false };
pub const found = WalkerMut{ .found_ = true };
pub const keep_walking = WalkerMut{ .keep_walking = true };
pub const stop = WalkerMut{ .keep_walking = false };
pub const found = WalkerMut{ .found = true };
const F = *const fn (ctx: *anyopaque, leaf: *const Leaf, metrics: Metrics) WalkerMut;
};
pub const Walker = struct {
keep_walking_: bool = false,
found_: bool = false,
keep_walking: bool = false,
found: bool = false,
err: ?anyerror = null,
pub const keep_walking = Walker{ .keep_walking_ = true };
pub const stop = Walker{ .keep_walking_ = false };
pub const found = Walker{ .found_ = true };
pub const keep_walking = Walker{ .keep_walking = true };
pub const stop = Walker{ .keep_walking = false };
pub const found = Walker{ .found = true };
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 {
var result = Walker{};
result.err = if (left.err) |_| left.err else right.err;
result.keep_walking_ = left.keep_walking_ and right.keep_walking_;
result.found_ = left.found_ or right.found_;
result.keep_walking = left.keep_walking and right.keep_walking;
result.found = left.found or right.found;
return result;
}
@ -144,8 +144,8 @@ pub const Branch = struct {
else
Node.new(allocator, new_left, new_right) catch |e| return .{ .err = e };
}
result.keep_walking_ = left.keep_walking_ and right.keep_walking_;
result.found_ = left.found_ or right.found_;
result.keep_walking = left.keep_walking and right.keep_walking;
result.found = left.found or right.found;
return result;
}
};
@ -350,10 +350,10 @@ const Node = union(enum) {
switch (self.*) {
.node => |*node| {
const left = node.left.walk_const(f, ctx, metrics);
if (!left.keep_walking_) {
if (!left.keep_walking) {
var result = Walker{};
result.err = left.err;
result.found_ = left.found_;
result.found = left.found;
return result;
}
const right = node.right.walk_const(f, ctx, metrics);
@ -367,10 +367,10 @@ const Node = union(enum) {
switch (self.*) {
.node => |*node| {
const left = node.left.walk(allocator, f, ctx, metrics);
if (!left.keep_walking_) {
if (!left.keep_walking) {
var result = WalkerMut{};
result.err = left.err;
result.found_ = left.found_;
result.found = left.found;
if (left.replace) |p| {
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)
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 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);
},
.leaf => |*l| {
if (line == 0) {
var result = f(ctx, l, metrics);
if (result.err) |_| return result;
result.found_ = true;
result.found = true;
return result;
}
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 {
const result = self.walk_from_line_begin_const_internal(line, f, ctx, metrics);
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 {
@ -420,8 +420,8 @@ const Node = union(enum) {
if (right_result.replace) |p| {
var result = WalkerMut{};
result.err = right_result.err;
result.found_ = right_result.found_;
result.keep_walking_ = right_result.keep_walking_;
result.found = right_result.found;
result.keep_walking = right_result.keep_walking;
result.replace = if (p.is_empty())
node.left
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 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);
},
.leaf => |*l| {
@ -442,7 +442,7 @@ const Node = union(enum) {
result.replace = null;
return result;
}
result.found_ = true;
result.found = true;
return result;
}
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 } {
const result = self.walk_from_line_begin_internal(allocator, line, f, ctx, metrics);
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 {
@ -508,12 +508,12 @@ const Node = union(enum) {
if (ret.err) |e| return .{ .err = e };
buf = buf[bytes..];
ctx.abs_col += @intCast(cols);
if (!ret.keep_walking_) return Walker.stop;
if (!ret.keep_walking) return Walker.stop;
}
if (leaf.eol) {
const ret = ctx.walker_f(ctx.walker_ctx, "\n", 1, metrics);
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;
}
return Walker.keep_walking;
@ -670,7 +670,7 @@ const Node = union(enum) {
var result = WalkerMut.keep_walking;
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.keep_walking_ = false;
result.keep_walking = false;
ctx.delete_next_bol = false;
return result;
}
@ -724,7 +724,7 @@ const Node = union(enum) {
}
}
if (ctx.bytes == 0 and !ctx.delete_next_bol)
result.keep_walking_ = false;
result.keep_walking = false;
}
return result;
}
@ -1211,9 +1211,6 @@ pub const LoadFromFileError = error{
DanglingSurrogateHalf,
ExpectedSecondSurrogateHalf,
UnexpectedSecondSurrogateHalf,
LockViolation,
ProcessNotFound,
Canceled,
};
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 {
var s = try ArrayList(u8).initCapacity(allocator, self.root.weights_sum().len);
try self.root.store(s.writer(), eol_mode);
@ -1310,7 +1302,6 @@ pub const StoreToFileError = error{
PathAlreadyExists,
PipeBusy,
ProcessFdQuotaExceeded,
ProcessNotFound,
ReadOnlyFileSystem,
RenameAcrossMountPoints,
SharingViolation,

View file

@ -13,14 +13,10 @@ pub const Context = struct {
args: tp.message = .{},
pub fn fmt(value: anytype) Context {
context_buffer.clearRetainingCapacity();
cbor.writeValue(context_buffer.writer(), value) catch @panic("command.Context.fmt failed");
return .{ .args = .{ .buf = context_buffer.items } };
return .{ .args = tp.message.fmtbuf(&context_buffer, value) catch @panic("command.Context.fmt failed") };
}
};
const context_buffer_allocator = std.heap.c_allocator;
threadlocal var context_buffer: std.ArrayList(u8) = std.ArrayList(u8).init(context_buffer_allocator);
threadlocal var context_buffer: [tp.max_message_size]u8 = undefined;
pub const fmt = Context.fmt;
const Vtable = struct {
@ -150,8 +146,13 @@ pub fn execute(id: ID, ctx: Context) tp.result {
}
pub fn get_id(name: []const u8) ?ID {
var id: ?ID = null;
return get_id_cache(name, &id);
for (commands.items) |cmd| {
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 {
@ -173,7 +174,6 @@ pub fn get_id_cache(name: []const u8, id: *?ID) ?ID {
return p.id;
};
}
tp.trace(tp.channel.debug, .{ "command", "get_id_cache", "failed", name });
return null;
}
@ -216,7 +216,7 @@ fn getTargetType(comptime Namespace: type) type {
fn getCommands(comptime Namespace: type) []const CmdDef(*getTargetType(Namespace)) {
@setEvalBranchQuota(10_000);
comptime switch (@typeInfo(Namespace)) {
.@"struct" => |info| {
.Struct => |info| {
var count = 0;
const Target = getTargetType(Namespace);
// @compileLog(Namespace, Target);
@ -257,14 +257,14 @@ pub fn Collection(comptime Namespace: type) type {
fields_var[i] = .{
.name = cmd.name,
.type = Clsr,
.default_value_ptr = null,
.default_value = null,
.is_comptime = false,
.alignment = if (@sizeOf(Clsr) > 0) @alignOf(Clsr) else 0,
};
}
const fields: [cmds.len]std.builtin.Type.StructField = fields_var;
const Fields = @Type(.{
.@"struct" = .{
.Struct = .{
.is_tuple = false,
.layout = .auto,
.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,
highlight_current_line: 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",
inline_diagnostics: bool = true,
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_fileicons: bool = true,
start_debugger_on_crash: bool = false,
lsp_request_timeout: usize = 10,
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+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 = r", "format"],
["ctrl+c l g g", "goto_definition"],
@ -66,7 +55,6 @@
["ctrl+c l g r", "references"],
["ctrl+c l h h", "hover"],
["ctrl+c l r r", "rename_symbol"],
["ctrl+c f", "copy_file_name"],
["super+l = =", "format"],
["super+l = r", "format"],

View file

@ -50,12 +50,10 @@
["ctrl+l", "scroll_view_center_cycle"],
["ctrl+n", "goto_next_match"],
["ctrl+p", "goto_prev_match"],
["ctrl+b", "move_to_char", "move_or_select_to_char_left"],
["ctrl+t", "move_to_char", "move_or_select_to_char_right"],
["ctrl+b", "move_to_char", "left"],
["ctrl+t", "move_to_char", "right"],
["ctrl+x", "cut"],
["ctrl+c", "copy"],
["alt+C", "copy_file_name"],
["ctrl+k alt+c", "copy_file_name", "file_name_only"],
["ctrl+v", "system_paste"],
["ctrl+u", "pop_cursor"],
["ctrl+k m", "change_file_type"],
@ -192,17 +190,6 @@
["«", "smart_insert_pair", "«", "»"],
["»", "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"],
["right_control", "enable_jump_mode"],
["left_alt", "enable_fast_scroll"],
@ -400,7 +387,7 @@
["f15", "goto_prev_match"],
["f9", "theme_prev"],
["f10", "theme_next"],
["escape", "mini_mode_cancel"],
["escape", "exit_mini_mode"],
["enter", "mini_mode_select"],
["backspace", "mini_mode_delete_backwards"]
]

View file

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

View file

@ -17,7 +17,7 @@
["B", "move_word_left"],
["e", "move_word_right_end_vim"],
["x", "cut_forward_internal"],
["s", ["enter_mode", "insert"], ["cut_forward_internal"]],
["s", ["cut_forward_internal"], ["enter_mode", "insert"]],
["u", "undo"],
["j", "move_down_vim"],
@ -27,11 +27,11 @@
["<Space>", "move_right_vim"],
["i", "enter_mode", "insert"],
["a", ["enter_mode", "insert"], ["move_right"]],
["I", ["enter_mode", "insert"], ["smart_move_begin"]],
["A", ["enter_mode", "insert"], ["move_end"]],
["o", ["enter_mode", "insert"], ["smart_insert_line_after"]],
["O", ["enter_mode", "insert"], ["smart_insert_line_before"]],
["a", ["move_right"], ["enter_mode", "insert"]],
["I", ["smart_move_begin"], ["enter_mode", "insert"]],
["A", ["move_end"], ["enter_mode", "insert"]],
["o", ["smart_insert_line_after"], ["enter_mode", "insert"]],
["O", ["smart_insert_line_before"], ["enter_mode", "insert"]],
["<S-.><S-.>", "indent"],
["<S-,><S-,>", "unindent"],
@ -41,6 +41,7 @@
["n", "goto_next_match"],
["N", "goto_prev_match"],
["0", "move_begin"],
["^", "smart_move_begin"],
["$", "move_end"],
[":", "open_command_palette"],
@ -51,7 +52,7 @@
["gd", "goto_definition"],
["gi", "goto_implementation"],
["gy", "goto_type_definition"],
["gg", "goto_line_vim"],
["gg", "move_buffer_begin"],
["grn", "rename_symbol"],
["gD", "goto_declaration"],
["G", "move_buffer_end"],
@ -62,11 +63,11 @@
["dd", "cut_internal_vim"],
["\"_dd", "delete_line"],
["cc", ["enter_mode", "insert"], ["cut_internal_vim"]],
["C", ["enter_mode", "insert"], ["cut_to_end_vim"]],
["cc", ["cut_internal_vim"], ["enter_mode", "insert"]],
["C", ["cut_to_end_vim"], ["enter_mode", "insert"]],
["D", "cut_to_end_vim"],
["cw", ["enter_mode", "insert"], ["cut_word_right_vim"]],
["cb", ["enter_mode", "insert"], ["cut_word_left_vim"]],
["cw", ["cut_word_right_vim"], ["enter_mode", "insert"]],
["cb", ["cut_word_left_vim"], ["enter_mode", "insert"]],
["yy", ["copy_line_internal_vim"], ["cancel"]],
@ -85,24 +86,11 @@
["<C-k>", "TODO"],
["F", "move_to_char", "move_to_char_left"],
["f", "move_to_char", "move_to_char_right"],
["T", "move_to_char", "move_till_char_left"],
["t", "move_to_char", "move_till_char_right"],
["F", "move_to_char", "left"],
["f", "move_to_char", "right"],
["<C-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]
["<CR>", ["move_down"], ["move_begin"]]
]
},
"visual": {
@ -112,7 +100,6 @@
"line_numbers": "relative",
"cursor": "block",
"selection": "normal",
"init_command": ["enable_selection"],
"press": [
["<Esc>", ["cancel"], ["enter_mode", "normal"]],
["k", "select_up"],
@ -128,13 +115,7 @@
["0", "move_begin"],
["^", "smart_move_begin"],
["$", "select_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"],
["$", "move_end"],
["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"]],
["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", ["enter_mode", "insert"], ["cut_to_end_vim"]],
["c", ["cut_forward_internal"], ["enter_mode", "insert"]],
["C", ["cut_to_end_vim"], ["enter_mode", "insert"]],
["D", "cut_to_end_vim"]
]
},
@ -172,7 +153,6 @@
["0", "move_begin"],
["^", "smart_move_begin"],
["$", "move_end"],
[":", "open_command_palette"],
["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"]],
["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", ["enter_mode", "insert"], ["cut_to_end_vim"]],
["c", ["cut_internal_vim"], ["enter_mode", "insert"]],
["C", ["cut_to_end_vim"], ["enter_mode", "insert"]],
["D", "cut_to_end_vim"]
]
},
@ -199,8 +179,6 @@
"name": "INSERT",
"line_numbers": "absolute",
"cursor": "beam",
"init_command": ["pause_undo_history"],
"deinit_command": ["resume_undo_history"],
"press": [
["<Esc>", ["move_left_vim"], ["enter_mode", "normal"]],
["<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") },
});
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 {
return Handler.create(mode_name, allocator, opts) catch |e| switch (e) {
error.NotFound => return error.Stop,
@ -86,8 +61,6 @@ const Handler = struct {
.line_numbers = self.bindings.line_numbers,
.cursor_shape = self.bindings.cursor_shape,
.selection_style = self.bindings.selection_style,
.init_command = self.bindings.init_command,
.deinit_command = self.bindings.deinit_command,
};
}
pub fn deinit(self: *@This()) void {
@ -109,20 +82,8 @@ pub const Mode = struct {
keybind_hints: *const KeybindHints,
cursor_shape: ?CursorShape = null,
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 {
if (self.deinit_command) |deinit_command|
deinit_command.execute_const();
self.allocator.free(self.mode);
self.input_handler.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 {
const new_namespace = try get_or_load_namespace(namespace_name);
if (globals.current_namespace) |old_namespace|
if (old_namespace.deinit_command) |deinit_command|
deinit_command.execute_const();
if (old_namespace.deinit_command) |deinit|
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;
if (new_namespace.init_command) |init_command|
init_command.execute_const();
if (new_namespace.init_command) |init|
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 {
@ -198,7 +167,7 @@ fn get_mode_binding_set(mode_name: []const u8, insert_command: []const u8) LoadE
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
const Namespace = struct {
@ -295,29 +264,13 @@ const Command = struct {
};
var buf: [2048]u8 = undefined;
@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] } });
}
fn execute_const(self: *const @This()) void {
fn execute_const(self: *const @This()) !void {
var buf: [2048]u8 = undefined;
@memcpy(buf[0..self.args.len], self.args);
command.executeName(self.command, .{ .args = .{ .buf = buf[0..self.args.len] } }) catch |e| {
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;
try command.executeName(self.command, .{ .args = .{ .buf = buf[0..self.args.len] } });
}
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,
insert_command: []const u8 = "",
hints_map: KeybindHints = .{},
init_command: ?Command = null,
deinit_command: ?Command = null,
const KeySyntax = enum { flow, vim };
const OnMatchFailure = enum { insert, ignore };
@ -440,8 +391,6 @@ const BindingSet = struct {
inherit: ?[]const u8 = null,
inherits: ?[][]const u8 = 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, .{
.ignore_unknown_fields = true,
@ -453,8 +402,6 @@ const BindingSet = struct {
self.line_numbers = parsed.value.line_numbers;
self.cursor_shape = parsed.value.cursor;
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.release, input.event.release, parsed.value.release);
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 };
}
pub fn current_integer_argument() ?usize {
return integer_argument;
}
pub fn clear_integer_argument() void {
integer_argument = null;
}
const expectEqual = std.testing.expectEqual;
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, '+');
loop: while (iter.next()) |part| {
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| {
if (std.mem.eql(u8, part, field.name)) {
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 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;

View file

@ -16,7 +16,7 @@ pub const Selection = struct {
end: Cursor = Cursor{},
};
pub fn create() error{ OutOfMemory, ThespianSpawnFailed }!Self {
pub fn create() !Self {
return .{ .pid = try Process.create() };
}
@ -45,7 +45,7 @@ const Process = struct {
selection: ?Selection = null,
};
pub fn create() error{ OutOfMemory, ThespianSpawnFailed }!tp.pid {
pub fn create() !tp.pid {
const self = try outer_a.create(Process);
self.* = .{
.arena = std.heap.ArenaAllocator.init(outer_a),
@ -123,13 +123,13 @@ const Process = struct {
if (isdupe(self.backwards.getLastOrNull(), entry)) {
if (self.current) |current| self.forwards.append(current) catch {};
if (self.backwards.pop()) |top|
self.allocator.free(top.file_path);
const top = self.backwards.pop();
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 }));
} else if (isdupe(self.forwards.getLastOrNull(), entry)) {
if (self.current) |current| self.backwards.append(current) catch {};
if (self.forwards.pop()) |top|
self.allocator.free(top.file_path);
const top = self.forwards.pop();
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 }));
} else if (self.current) |current| {
try self.backwards.append(current);

View file

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

View file

@ -1,11 +1,10 @@
const std = @import("std");
const tui = @import("tui");
const cbor = @import("cbor");
const thespian = @import("thespian");
const flags = @import("flags");
const builtin = @import("builtin");
const bin_path = @import("bin_path");
const bin_path = @import("bin_path.zig");
const list_languages = @import("list_languages.zig");
pub const file_link = @import("file_link.zig");
@ -16,7 +15,6 @@ const c = @cImport({
const build_options = @import("build_options");
const log = @import("log");
pub const version = @embedFile("version");
pub const version_info = @embedFile("version_info");
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_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) .info else .warn,
.logFn = log.std_log_function,
@ -35,11 +33,7 @@ pub const std_options: std.Options = .{
const renderer = @import("renderer");
pub const panic = if (@hasDecl(renderer, "panic")) renderer.panic else default_panic;
fn default_panic(msg: []const u8, _: ?*std.builtin.StackTrace, ret_addr: ?usize) noreturn {
return std.debug.defaultPanic(msg, ret_addr);
}
pub const panic = if (@hasDecl(renderer, "panic")) renderer.panic else std.builtin.default_panic;
pub fn main() anyerror!void {
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
\\file name to specify a specific location, or pass +<LINE> separately to set the line.
;
;
pub const descriptions = .{
.project = "Set project directory (default: cwd)",
@ -305,27 +299,7 @@ pub fn main() anyerror!void {
if (args.exec) |exec_str| {
var cmds = std.mem.splitScalar(u8, exec_str, ';');
while (cmds.next()) |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 });
}
while (cmds.next()) |cmd| try tui_proc.send(.{ "cmd", cmd, .{} });
}
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;
fn trace_to_file(m: thespian.message.c_buffer_type) callconv(.C) void {
const cbor = @import("cbor");
const State = struct {
file: std.fs.File,
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 {
const cbor = @import("cbor");
var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only });
defer file.close();
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 {
const cbor = @import("cbor");
var file = try std.fs.openFileAbsolute(file_name, .{ .mode = .read_only });
defer file.close();
const json = try file.readToEndAlloc(allocator, 64 * 1024);
@ -521,6 +498,7 @@ fn read_cbor_config(
file_name: []const u8,
cb: []const u8,
) !void {
const cbor = @import("cbor");
var iter = cb;
var field_name: []const u8 = undefined;
while (cbor.matchString(&iter, &field_name) catch |e| switch (e) {
@ -528,7 +506,7 @@ fn read_cbor_config(
else => return e,
}) {
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 (std.mem.eql(u8, field_name, field_info.name)) {
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) (ConfigDirError || ConfigWriteError)!void {
pub fn write_config(conf: anytype, allocator: std.mem.Allocator) !void {
config_mutex.lock();
defer config_mutex.unlock();
_ = 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))));
}
fn write_text_config_file(comptime T: type, data: T, file_name: []const u8) ConfigWriteError!void {
var file = std.fs.createFileAbsolute(file_name, .{ .truncate = true }) catch |e| {
std.log.err("createFileAbsolute failed with {any} for: {s}", .{ e, file_name });
return error.CreateConfigFileFailed;
};
fn write_text_config_file(comptime T: type, data: T, file_name: []const u8) !void {
var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true });
defer file.close();
const writer = file.writer();
write_config_to_writer(T, data, writer) catch |e| {
std.log.err("write file failed with {any} for: {s}", .{ e, file_name });
return error.WriteConfigFileFailed;
};
return write_config_to_writer(T, data, writer);
}
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 = .{};
inline for (@typeInfo(T).@"struct".fields) |field_info| {
inline for (@typeInfo(T).Struct.fields) |field_info| {
if (config_eql(
field_info.type,
@field(data, field_info.name),
@ -617,8 +587,8 @@ fn config_eql(comptime T: type, a: T, b: T) bool {
else => {},
}
switch (@typeInfo(T)) {
.bool, .int, .float, .@"enum" => return a == b,
.optional => |info| {
.Bool, .Int, .Float, .Enum => return a == b,
.Optional => |info| {
if (a == null and b == null)
return true;
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 {
const cbor = @import("cbor");
var file = try std.fs.createFileAbsolute(file_name, .{ .truncate = true });
defer file.close();
@ -671,47 +642,11 @@ pub fn list_keybind_namespaces(allocator: std.mem.Allocator) ![]const []const u8
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 {
return get_app_config_dir(application_name);
}
pub const ConfigDirError = error{
NoSpaceLeft,
MakeConfigDirFailed,
MakeHomeConfigDirFailed,
MakeAppConfigDirFailed,
AppConfigDirUnavailable,
};
fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
fn get_app_config_dir(appname: []const u8) ![]const u8 {
const a = std.heap.c_allocator;
const local = struct {
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});
std.fs.makeDirAbsolute(dir) catch |e| switch (e) {
error.PathAlreadyExists => {},
else => return error.MakeHomeConfigDirFailed,
else => return e,
};
break :ret try std.fmt.bufPrint(&local.config_dir_buffer, "{s}/.config/{s}", .{ home, appname });
} 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 });
std.fs.makeDirAbsolute(dir) catch |e| switch (e) {
error.PathAlreadyExists => {},
else => return error.MakeAppConfigDirFailed,
else => return e,
};
break :ret dir;
} else return error.AppConfigDirUnavailable;
@ -745,15 +680,12 @@ fn get_app_config_dir(appname: []const u8) ConfigDirError![]const u8 {
local.config_dir = config_dir;
std.fs.makeDirAbsolute(config_dir) catch |e| switch (e) {
error.PathAlreadyExists => {},
else => return error.MakeConfigDirFailed,
else => return e,
};
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 {};
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;
}
@ -848,11 +780,11 @@ fn get_app_state_dir(appname: []const u8) ![]const u8 {
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");
}
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 {
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 });
}
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 {
var executable: [:0]const u8 = std.mem.span(std.os.argv[0]);
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 });
}
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");
if (project.len == 0)
return error.NoProject;
const cp = @import("completion.zig");
return cp.send(allocator, (try get()).pid, .{ "get_mru_position", project, file_path }, ctx);
const rsp = try (try get()).pid.call(allocator, request_timeout, .{ "get_mru_position", project, file_path });
defer allocator.free(rsp.buf);
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 {
@ -325,7 +328,6 @@ const Process = struct {
var text_len: usize = 0;
var n: usize = 0;
var task: []const u8 = undefined;
var context: usize = undefined;
var root_dst: usize = 0;
var root_src: usize = 0;
@ -342,9 +344,6 @@ const Process = struct {
if (self.walker) |pid| pid.deinit();
self.walker = null;
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) })) {
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) })) {
@ -427,7 +426,6 @@ const Process = struct {
self.walker = try walk_tree_async(self.allocator, project_directory);
self.restore_project(project) catch |e| self.logger.err("restore_project", e);
project.sort_files_by_mtime();
project.query_git();
} else {
self.logger.print("switched to: {s}", .{project_directory});
}
@ -926,11 +924,10 @@ const FilteredWalker = struct {
var containing = top;
var dirname_len = top.dirname_len;
if (top.iter.next() catch {
var item_ = self.stack.pop();
if (item_) |*item|
if (self.stack.items.len != 0) {
item.iter.dir.close();
};
var item = self.stack.pop();
if (self.stack.items.len != 0) {
item.iter.dir.close();
}
continue;
}) |base| {
self.name_buffer.shrinkRetainingCapacity(dirname_len);
@ -961,11 +958,10 @@ const FilteredWalker = struct {
else => continue,
}
} else {
var item_ = self.stack.pop();
if (item_) |*item|
if (self.stack.items.len != 0) {
item.iter.dir.close();
};
var item = self.stack.pop();
if (self.stack.items.len != 0) {
item.iter.dir.close();
}
}
}
return null;

View file

@ -76,16 +76,8 @@ pub fn columns(self: *const Cell) usize {
}
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.bg = apply_alpha_value(self.cell.style.bg, alpha);
}
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 {
const opts: vaxis.Window.ChildOptions = .{
const opts = .{
.x_off = @as(i17, @intCast(nopts.x)),
.y_off = @as(i17, @intCast(nopts.y)),
.width = @as(u16, @intCast(nopts.cols)),

View file

@ -2,15 +2,13 @@ const vaxis = @import("vaxis");
const meta = @import("std").meta;
const utf8Encode = @import("std").unicode.utf8Encode;
const utf8Decode = @import("std").unicode.utf8Decode;
const utf8ValidateSlice = @import("std").unicode.utf8ValidateSlice;
const FormatOptions = @import("std").fmt.FormatOptions;
pub const key = vaxis.Key;
pub const Key = u21;
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 MOTION: Mouse = vaxis.Mouse.Button.none;
@ -76,7 +74,6 @@ pub const KeyEvent = struct {
}
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 other_mods = other.mods_no_caps();
return self.key_unshifted == other.key_unshifted and self_mods == other_mods;
@ -120,7 +117,7 @@ pub const KeyEvent = struct {
pub fn from_message(
event_: Event,
keypress_: Key,
keypress_shifted_: Key,
keypress_shifted: Key,
text: []const u8,
modifiers: Mods,
) @This() {
@ -131,11 +128,6 @@ pub const KeyEvent = struct {
key.left_alt, key.right_alt => modifiers & ~mod.alt,
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_)
map_key_to_unshifed_legacy(keypress_shifted, mods_)
else

View file

@ -12,7 +12,6 @@ pub const Cell = @import("Cell.zig");
pub const CursorShape = vaxis.Cell.CursorShape;
pub const style = @import("style.zig").StyleBits;
pub const styles = @import("style.zig");
const Self = @This();
pub const log_name = "vaxis";
@ -26,7 +25,6 @@ no_alternate: bool,
event_buffer: std.ArrayList(u8),
input_buffer: std.ArrayList(u8),
mods: vaxis.Key.Modifiers = .{},
queries_done: bool,
bracketed_paste: bool = false,
bracketed_paste_buffer: std.ArrayList(u8),
@ -41,25 +39,7 @@ logger: log.Logger,
loop: Loop,
pub const Error = error{
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 {
pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool, _: *const fn (ctx: *anyopaque) void) !Self {
const opts: vaxis.Vaxis.Options = .{
.kitty_keyboard_flags = .{
.disambiguate = true,
@ -72,7 +52,7 @@ pub fn init(allocator: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate:
};
return .{
.allocator = allocator,
.tty = vaxis.Tty.init() catch return error.TtyInitError,
.tty = try vaxis.Tty.init(),
.vx = try vaxis.init(allocator, opts),
.no_alternate = no_alternate,
.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,
.logger = log.logger(log_name),
.loop = undefined,
.queries_done = false,
};
}
@ -101,29 +80,28 @@ var panic_cleanup: ?struct {
vx: *vaxis.Vaxis,
} = null;
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;
panic_cleanup = null;
if (cleanup) |self| {
self.vx.deinit(self.allocator, self.tty.anyWriter());
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.conpty_hacks = true;
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) {
try self.resize(.{ .rows = 25, .cols = 80, .x_pixel = 0, .y_pixel = 0 }); // dummy resize to fully init vaxis
} else {
self.sigwinch() catch return error.TtyWriteError;
try self.sigwinch();
}
self.vx.setBracketedPaste(self.tty.anyWriter(), true) catch return error.TtyWriteError;
self.vx.queryTerminalSend(self.tty.anyWriter()) catch return error.TtyWriteError;
try self.vx.setBracketedPaste(self.tty.anyWriter(), true);
try self.vx.queryTerminalSend(self.tty.anyWriter());
self.loop = Loop.init(&self.tty, &self.vx);
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()));
}
fn resize(self: *Self, ws: vaxis.Winsize) error{ TtyWriteError, OutOfMemory }!void {
self.vx.resize(self.allocator, self.tty.anyWriter(), ws) catch return error.TtyWriteError;
fn resize(self: *Self, ws: vaxis.Winsize) !void {
try self.vx.resize(self.allocator, self.tty.anyWriter(), ws);
self.vx.queueRefresh();
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;
}
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 text_: []const u8 = undefined;
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_);
switch (event.*) {
.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__));
try self.sync_mod_state(key_.codepoint, key_.mods);
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;
},
.cap_da1 => {
self.queries_done = true;
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 => {
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();
try cbor.writeValue(self.event_buffer.writer(), value);
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 }));
}
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.bracketed_paste_buffer.clearAndFree();
self.bracketed_paste = false;
@ -526,7 +494,7 @@ const Loop = struct {
}
/// 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;
self.thread = try std.Thread.spawn(.{}, Loop.ttyRun, .{self});
}

View file

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

View file

@ -18,55 +18,8 @@ pub const StyleBits = @import("tuirenderer").style;
const gui = @import("gui");
const DropWriter = gui.DropWriter;
pub const style = StyleBits;
pub const styles = @import("tuirenderer").styles;
pub const Error = error{
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;
}
pub const panic = win32.messageBoxThenPanic(.{ .title = "Flow Panic" });
allocator: std.mem.Allocator,
vx: vaxis.Vaxis,
@ -82,7 +35,7 @@ thread: ?std.Thread = null,
hwnd: ?win32.HWND = null,
title_buf: std.ArrayList(u16),
style_: ?Style = null,
style: ?Style = null,
const global = struct {
var init_called: bool = false;
@ -97,7 +50,7 @@ pub fn init(
handler_ctx: *anyopaque,
no_alternate: bool,
dispatch_initialized: *const fn (ctx: *anyopaque) void,
) Error!Self {
) !Self {
std.debug.assert(!global.init_called);
global.init_called = true;
@ -133,16 +86,16 @@ pub fn deinit(self: *Self) void {
self.title_buf.deinit();
}
pub fn run(self: *Self) Error!void {
pub fn run(self: *Self) !void {
if (self.thread) |_| return;
// dummy resize to fully init vaxis
const drop_writer = DropWriter{};
self.vx.resize(
try self.vx.resize(
self.allocator,
drop_writer.writer().any(),
.{ .rows = 25, .cols = 80, .x_pixel = 0, .y_pixel = 0 },
) catch return error.VaxisResizeError;
);
self.thread = try gui.start();
}
@ -181,7 +134,7 @@ pub fn stdplane(self: *Self) 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 {
kind: u8,
codepoint: u21,
@ -391,12 +344,12 @@ fn update_window_title(self: *Self) void {
}
pub fn set_terminal_style(self: *Self, style_: Style) void {
self.style_ = style_;
self.style = style_;
self.update_window_style();
}
fn update_window_style(self: *Self) void {
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));
}
}

View file

@ -134,7 +134,7 @@ const Process = struct {
fn receive(self: *Process, _: tp.pid_ref, m: tp.message) tp.result {
errdefer self.deinit();
var bytes: []const u8 = "";
var bytes: []u8 = "";
if (try m.match(.{ "input", tp.extract(&bytes) })) {
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);
}

View file

@ -21,11 +21,6 @@ pub const Error = error{
IntegerTooSmall,
InvalidType,
TooShort,
InvalidFloatType,
InvalidArrayType,
InvalidPIntType,
JsonIncompatibleType,
NotAnObject,
};
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,
err: ?*const OutputHandler = null,
exit: *const ExitHandler = log_exit_handler,
log_execute: bool = true,
};
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;
_ = arg0;
const logger = log.logger(@typeName(Self));
defer logger.deinit();
var it = std.mem.splitScalar(u8, output, '\n');
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;
_ = parent;
const logger = log.logger(@typeName(Self));
defer logger.deinit();
var it = std.mem.splitScalar(u8, output, '\n');
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;
_ = parent;
const logger = log.logger(@typeName(Self));
defer logger.deinit();
if (exit_code > 0) {
logger.print_err(arg0, "'{s}' terminated {s} exitcode: {d}", .{ arg0, err_msg, exit_code });
} else {
@ -126,7 +117,6 @@ pub fn log_exit_err_handler(context: usize, parent: tp.pid_ref, arg0: []const u8
_ = context;
_ = parent;
const logger = log.logger(@typeName(Self));
defer logger.deinit();
if (exit_code > 0) {
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);
var buf: [1024]u8 = undefined;
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());
tp.receive(&self.receiver);
}
fn receive(self: *Process, _: tp.pid_ref, m: tp.message) tp.result {
errdefer self.deinit();
var bytes: []const u8 = "";
var bytes: []u8 = "";
if (try m.match(.{ "input", tp.extract(&bytes) })) {
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 optimize = b.standardOptimizeOption(.{});
const tree_sitter_dep = b.dependency("tree_sitter", .{
const tree_sitter_dep = b.dependency("tree-sitter", .{
.target = target,
.optimize = optimize,
});
const tree_sitter_host_dep = b.dependency("tree_sitter", .{
.target = b.graph.host,
.optimize = optimize,
});
const imports: []const std.Build.Module.Import = if (use_tree_sitter) &.{
.{ .name = "build_options", .module = options_mod },
.{ .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", .{
.target = target,
.optimize = optimize,
});
ts_queryfile(b, tree_sitter_dep, "queries/cmake/injections.scm"),
ts_queryfile(b, tree_sitter_dep, "tree-sitter-astro/queries/injections.scm"),
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(.{
.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", .{
_ = b.addModule("syntax", .{
.root_source_file = b.path("src/syntax.zig"),
.imports = &.{
.{ .name = "build_options", .module = options_mod },
.{ .name = "cbor", .module = cbor_dep.module("cbor") },
.{ .name = "treez", .module = tree_sitter_dep.module("treez") },
},
.imports = imports,
});
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 {
const module = b.createModule(.{ .root_source_file = dep.path(sub_path) });
bin_gen.root_module.addImport(sub_path, module);
fn ts_queryfile(b: *std.Build, dep: *std.Build.Dependency, comptime sub_path: []const u8) std.Build.Module.Import {
return .{
.name = sub_path,
.module = b.createModule(.{
.root_source_file = dep.path(sub_path),
}),
};
}

View file

@ -1,17 +1,11 @@
.{
.name = .flow_syntax,
.version = "0.1.0",
.fingerprint = 0x3ba2584ea1cec85f,
.minimum_zig_version = "0.14.0-dev.3451+d8d2aa9af",
.name = "flow-syntax",
.version = "0.0.1",
.dependencies = .{
.tree_sitter = .{
.@"tree-sitter" = .{
.url = "https://github.com/neurocyte/tree-sitter/releases/download/master-86dd4d2536f2748c5b4ea0e1e70678039a569aac/source.tar.gz",
.hash = "N-V-__8AACablCbp-6lsRoKDEp6Xd2dHLe4AsW81blkSQxzs",
},
.cbor = .{
.url = "https://github.com/neurocyte/cbor/archive/1fccb83c70cd84e1dff57cc53f7db8fb99909a94.tar.gz",
.hash = "cbor-1.0.0-RcQE_HvqAACcrLH7t3IDZOshgY2xqJA_UX330MvwSepb",
.hash = "1220e9fba96c468283129e977767472dee00b16f356e5912431cec8f1a009b6691a2",
},
},
.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 cbor = @import("cbor");
const build_options = @import("build_options");
const treez = if (build_options.use_tree_sitter)
@ -15,6 +14,8 @@ name: []const u8,
description: []const u8,
lang_fn: LangFn,
extensions: []const []const u8,
highlights: [:0]const u8,
injections: ?[:0]const u8,
first_line_matches: ?FirstLineMatch = null,
comment: []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;
pub const FirstLineMatch = struct {
const FirstLineMatch = struct {
prefix: ?[]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 {
comptime switch (@typeInfo(Namespace)) {
.@"struct" => |info| {
.Struct => |info| {
var count = 0;
for (info.decls) |_| {
// @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),
.extensions = vec(args.extensions),
.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,
.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,
@ -135,63 +152,3 @@ fn load_file_types(comptime Namespace: type) []const FileType {
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 = .{
.description = "Agda",
.extensions = .{"agda"},
@ -21,7 +18,7 @@ pub const bash = .{
.icon = "󱆃",
.extensions = .{ "sh", "bash", ".profile" },
.comment = "#",
.first_line_matches = FirstLineMatch{ .prefix = "#!", .content = "sh" },
.first_line_matches = .{ .prefix = "#!", .content = "sh" },
.formatter = .{ "shfmt", "--indent", "4" },
.language_server = .{ "bash-language-server", "start" },
};
@ -48,7 +45,7 @@ pub const conf = .{
.description = "Config",
.color = 0x000000,
.icon = "",
.extensions = .{ "conf", "log", "config", ".gitconfig", "gui_config" },
.extensions = .{ "conf", "config", ".gitconfig", "gui_config" },
.highlights = fish.highlights,
.comment = "#",
.parser = fish.parser,
@ -256,7 +253,7 @@ pub const lua = .{
.extensions = .{"lua"},
.comment = "--",
.injections = "tree-sitter-lua/queries/injections.scm",
.first_line_matches = FirstLineMatch{ .prefix = "--", .content = "lua" },
.first_line_matches = .{ .prefix = "--", .content = "lua" },
.language_server = .{"lua-lsp"},
};
@ -266,7 +263,7 @@ pub const mail = .{
.extensions = .{ "eml", "mbox" },
.comment = ">",
.highlights = "tree-sitter-mail/queries/mail/highlights.scm",
.first_line_matches = FirstLineMatch{ .prefix = "From" },
.first_line_matches = .{ .prefix = "From" },
};
pub const make = .{
@ -410,7 +407,7 @@ pub const python = .{
.icon = "󰌠",
.extensions = .{ "py", "pyi" },
.comment = "#",
.first_line_matches = FirstLineMatch{ .prefix = "#!", .content = "python" },
.first_line_matches = .{ .prefix = "#!", .content = "python" },
.language_server = .{"pylsp"},
};
@ -523,7 +520,7 @@ pub const xml = .{
.extensions = .{"xml"},
.comment = "<!--",
.highlights = "tree-sitter-xml/queries/xml/highlights.scm",
.first_line_matches = FirstLineMatch{ .prefix = "<?xml " },
.first_line_matches = .{ .prefix = "<?xml " },
.formatter = .{ "xmllint", "--format", "-" },
};

View file

@ -10,7 +10,6 @@ const Self = @This();
pub const Edit = treez.InputEdit;
pub const FileType = @import("file_type.zig");
pub const QueryCache = @import("QueryCache.zig");
pub const Range = treez.Range;
pub const Point = treez.Point;
const Input = treez.Input;
@ -24,40 +23,37 @@ lang: *const Language,
file_type: *const FileType,
parser: *Parser,
query: *Query,
injections: ?*Query,
injections: *Query,
tree: ?*treez.Tree = null,
pub fn create(file_type: *const FileType, allocator: std.mem.Allocator, query_cache: *QueryCache) !*Self {
const query = try query_cache.get(file_type, .highlights);
const injections = try query_cache.get(file_type, .injections);
pub fn create(file_type: *const FileType, allocator: std.mem.Allocator) !*Self {
const self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.lang = file_type.lang_fn() orelse std.debug.panic("tree-sitter parser function failed for language: {s}", .{file_type.name}),
.file_type = file_type,
.parser = try Parser.create(),
.query = query,
.injections = injections,
.query = try Query.create(self.lang, file_type.highlights),
.injections = try Query.create(self.lang, file_type.highlights),
};
errdefer self.destroy(query_cache);
errdefer self.destroy();
try self.parser.setLanguage(self.lang);
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;
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;
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();
query_cache.release(self.query, .highlights);
if (self.injections) |injections| query_cache.release(injections, .injections);
self.query.destroy();
self.parser.destroy();
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 {
const impl = @typeInfo(@TypeOf(pimpl));
const child: type = impl.pointer.child;
const child: type = impl.Pointer.child;
return .{
.ptr = pimpl,
.vtable = comptime &.{

View file

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

View file

@ -23,7 +23,7 @@ plane: Plane,
parent: Plane,
allocator: Allocator,
widgets: ArrayList(WidgetState),
layout_: Layout,
layout: Layout,
direction: Direction,
box: ?Widget.Box = 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,
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);
self.* = try init(allocator, parent, name, .horizontal, layout_, Box{});
self.plane.hide();
@ -58,7 +58,7 @@ fn init(allocator: Allocator, parent: Plane, name: [:0]const u8, dir: Direction,
.parent = parent,
.allocator = allocator,
.widgets = ArrayList(WidgetState).init(allocator),
.layout_ = layout_,
.layout = layout_,
.direction = dir,
};
}
@ -68,7 +68,7 @@ pub fn widget(self: *Self) Widget {
}
pub fn layout(self: *Self) Widget.Layout {
return self.layout_;
return self.layout;
}
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 {
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 {

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 style = @import("renderer").style;
const styles = @import("renderer").styles;
const input = @import("input");
const command = @import("command");
const EventHandler = @import("EventHandler");
@ -34,7 +33,7 @@ highlight: bool,
symbols: bool,
width: usize = 4,
editor: *ed.Editor,
diff_: diff.AsyncDiffer,
diff: diff.AsyncDiffer,
diff_symbols: std.ArrayList(Symbol),
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,
.symbols = tui.config().gutter_symbols,
.editor = editor,
.diff_ = try diff.create(),
.diff = try diff.create(),
.diff_symbols = std.ArrayList(Symbol).init(allocator),
};
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.line + 1) {
self.plane.set_style(.{ .fg = theme.editor_gutter_active.fg });
self.plane.on_styles(styles.bold);
self.plane.on_styles(style.bold);
} else {
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.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 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;
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 {
@ -439,7 +438,7 @@ fn int_width(n_: usize) usize {
fn print_digits(self: *Self, n_: anytype, style_: DigitStyle) !void {
var n = n_;
var buf: [12][]const u8 = undefined;
var digits: std.ArrayListUnmanaged([]const u8) = .initBuffer(&buf);
var digits = std.ArrayListUnmanaged([]const u8).initBuffer(&buf);
while (true) {
digits.addOneAssumeCapacity().* = get_digit(n % 10, style_);
n /= 10;

View file

@ -263,34 +263,33 @@ fn select_next(self: *Self, dir: enum { up, down }) void {
const cmds = struct {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn goto_prev_file(self: *Self, _: Ctx) Result {
self.select_next(.up);
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 {
self.select_next(.down);
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 {
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 {
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 {
if (self.menu.selected == null) return tp.exit_error(error.NoSelectedFile, @errorReturnTrace());
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_digtl: [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 log = @import("log");
const cbor = @import("cbor");
const builtin = @import("builtin");
const Plane = @import("renderer").Plane;
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);
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);
return more or self.fire != null;
}
@ -359,29 +342,28 @@ const Commands = command.Collection(cmds);
const cmds = struct {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn save_all(_: *Self, _: Ctx) Result {
if (tui.get_buffer_manager()) |bm|
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 {
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 {
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 {
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 {
self.fire = if (self.fire) |*fire| ret: {
@ -389,7 +371,7 @@ const cmds = struct {
break :ret null;
} else try Fire.init(self.allocator, self.plane);
}
pub const home_sheeran_meta: Meta = .{};
pub const home_sheeran_meta = .{};
};
const Fire = @import("Fire.zig");

View file

@ -7,7 +7,6 @@ const syntax = @import("syntax");
const Plane = @import("renderer").Plane;
const style = @import("renderer").style;
const styles = @import("renderer").styles;
const EventHandler = @import("EventHandler");
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 {
if (font) |fs| switch (fs) {
.normal => {
self.plane.set_styles(styles.normal);
self.plane.set_styles(style.normal);
_ = self.plane.print(" normal", .{}) catch return;
},
.bold => {
self.plane.set_styles(styles.bold);
self.plane.set_styles(style.bold);
_ = self.plane.print(" bold", .{}) catch return;
},
.italic => {
self.plane.set_styles(styles.italic);
self.plane.set_styles(style.italic);
_ = self.plane.print(" italic", .{}) catch return;
},
.underline => {
self.plane.set_styles(styles.underline);
self.plane.set_styles(style.underline);
_ = self.plane.print(" underline", .{}) catch return;
},
.undercurl => {
self.plane.set_styles(styles.undercurl);
self.plane.set_styles(style.undercurl);
_ = self.plane.print(" undercurl", .{}) catch return;
},
.strikethrough => {
self.plane.set_styles(styles.struck);
self.plane.set_styles(style.struck);
_ = self.plane.print(" strikethrough", .{}) catch return;
},
};
self.plane.set_styles(styles.normal);
self.plane.set_styles(style.normal);
}
fn reset_style(self: *Self) void {

View file

@ -48,7 +48,7 @@ views_widget: Widget,
active_view: ?usize = 0,
panels: ?*WidgetList = null,
last_match_text: ?[]const u8 = null,
location_history_: location_history,
location_history: location_history,
buffer_manager: Buffer.Manager,
find_in_files_state: enum { init, adding, done } = .done,
file_list_type: FileListType = .find_in_files,
@ -60,9 +60,7 @@ const FileListType = enum {
find_in_files,
};
pub const CreateError = error{ OutOfMemory, ThespianSpawnFailed };
pub fn create(allocator: std.mem.Allocator) CreateError!Widget {
pub fn create(allocator: std.mem.Allocator) !Widget {
const self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
@ -70,7 +68,7 @@ pub fn create(allocator: std.mem.Allocator) CreateError!Widget {
.widgets = undefined,
.widgets_widget = undefined,
.floating_views = WidgetStack.init(allocator),
.location_history_ = try location_history.create(),
.location_history = try location_history.create(),
.views = undefined,
.views_widget = undefined,
.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_pos: usize = 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) })) {
try self.add_find_in_files_result(.references, path, begin_line, begin_pos, end_line, end_pos, lines, .Information);
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 },
});
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);
}
@ -193,7 +180,7 @@ fn bottom_bar_primary_drag(self: *Self, y: usize) tp.result {
};
const h = self.plane.dim_y();
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) {
self.panel_height = null;
command.executeName("toggle_panel", .{}) catch {};
@ -273,19 +260,19 @@ const cmds = struct {
try self.check_all_not_dirty();
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 {
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 {
try project_manager.open(".");
if (self.top_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 {
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.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 {
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|
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 {
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 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) {
const ctx_: struct {
allocator: std.mem.Allocator,
from: tp.pid,
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),
if (!same_file and !have_editor_metadata and line == null)
if (try project_manager.get_mru_position(self.allocator, f)) |pos| {
line = @intCast(pos.row);
column = @intCast(pos.col);
};
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 (self.get_active_editor()) |editor| {
editor.send_editor_jump_source() catch {};
@ -438,6 +395,7 @@ const cmds = struct {
}
tui.need_render();
}
pub const navigate_meta = .{ .arguments = &.{.object} };
pub fn open_help(self: *Self, _: Ctx) Result {
tui.reset_drag_context();
@ -445,7 +403,7 @@ const cmds = struct {
try command.executeName("open_scratch_buffer", command.fmt(.{ "help", @embedFile("help.md"), "markdown" }));
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 {
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" }));
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 {
tui.reset_drag_context();
@ -461,19 +419,19 @@ const cmds = struct {
try command.executeName("open_scratch_buffer", command.fmt(.{ "version", root.version_info, "diff" }));
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 {
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] } });
}
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 {
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] } });
}
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 {
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 });
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 {
var n: usize = 1;
@ -530,7 +488,7 @@ const cmds = struct {
logger.print("deleted buffer {s}", .{file_path});
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 {
var file_path: []const u8 = undefined;
@ -546,7 +504,7 @@ const cmds = struct {
_ = self.buffer_manager.close_buffer(buffer);
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 {
if (tp.env.get().str("project").len == 0) {
@ -556,7 +514,7 @@ const cmds = struct {
try self.read_restore_info();
tui.need_render();
}
pub const restore_session_meta: Meta = .{};
pub const restore_session_meta = .{};
pub fn toggle_panel(self: *Self, _: Ctx) Result {
if (self.is_panel_view_showing(logview))
@ -570,58 +528,52 @@ const cmds = struct {
else
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 {
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 {
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 {
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 {
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 {
try self.toggle_panel_view(@import("inspector_view.zig"), true);
}
pub const show_inspector_view_meta: 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 const show_inspector_view_meta = .{};
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 {
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 {
return self.create_home();
}
pub const show_home_meta: Meta = .{};
pub const show_home_meta = .{};
pub fn add_split(self: *Self, _: Ctx) Result {
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 {
const config = tui.config_mut();
@ -638,7 +590,7 @@ const cmds = struct {
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 {
const config = tui.config_mut();
@ -673,7 +625,7 @@ const cmds = struct {
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 {
if (self.is_panel_view_showing(filelist_view)) {
@ -685,7 +637,7 @@ const cmds = struct {
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 {
var file_path: []const u8 = undefined;
@ -720,7 +672,7 @@ const cmds = struct {
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 {
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_elem_meta: Meta = .{ .arguments = &.{ .string, .integer, .integer, .integer, .integer, .string } };
pub const rename_symbol_item_meta = .{ .arguments = &.{.array} };
pub const rename_symbol_item_elem_meta = .{ .arguments = &.{ .string, .integer, .integer, .integer, .integer, .string } };
pub fn clear_diagnostics(self: *Self, ctx: Ctx) Result {
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))
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 {
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 {
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 {
if (builtin.os.tag == .windows) {
@ -811,7 +763,7 @@ const cmds = struct {
}
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 {
var query: []const u8 = undefined;
@ -825,7 +777,7 @@ const cmds = struct {
defer rg.deinit();
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 {
if (!try ctx.args.match(.{ tp.string, tp.more }))
@ -837,7 +789,7 @@ const cmds = struct {
.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 {
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 });
}
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 {
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);
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 {
var buffer_ref: usize = 0;
@ -913,7 +865,7 @@ const cmds = struct {
buffer.update(root_);
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 {
var buffer_ref: usize = 0;
@ -927,7 +879,7 @@ const cmds = struct {
buffer.mark_clean();
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 {
var amount: f32 = undefined;
@ -936,7 +888,7 @@ const cmds = struct {
if (build_options.gui)
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 {
var fontsize: f32 = undefined;
@ -945,13 +897,13 @@ const cmds = struct {
if (build_options.gui)
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 {
if (build_options.gui)
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 {
var fontface: []const u8 = undefined;
@ -960,23 +912,23 @@ const cmds = struct {
if (build_options.gui)
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 {
if (build_options.gui)
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 {
_ = 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 {
_ = 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 {
@ -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 (row == 0 and col == 0) return;
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 = .{};
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 {};
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 log = @import("log");
const tp = @import("thespian");
const location_history = @import("location_history");
const command = @import("command");
const cmd = command.executeName;
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;
@ -27,39 +21,38 @@ const Commands = command.Collection(cmds_);
const cmds_ = struct {
pub const Target = void;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn w(_: *void, _: Ctx) Result {
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 {
try cmd("quit", .{});
}
pub const q_meta: Meta = .{ .description = "q (quit)" };
pub const q_meta = .{ .description = "q (quit)" };
pub fn @"q!"(_: *void, _: Ctx) Result {
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 {
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 {
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 {
cmd("save_file", .{}) catch {};
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 {
const logger = log.logger("helix-mode");
@ -72,279 +65,10 @@ const cmds_ = struct {
.begin = .{ .row = sel.begin.row, .col = sel.begin.col },
.end = .{ .row = sel.end.row, .col = sel.end.col },
} else null;
mv.location_history_.update(file_path, .{
mv.location_history.update(file_path, .{
.row = primary.cursor.row + 1,
.col = primary.cursor.col + 1,
}, sel);
}
pub const save_selection_meta: 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" };
pub const save_selection_meta = .{ .description = "Save current selection to location history" };
};
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 {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn mini_mode_reset(self: *Self, _: Ctx) Result {
self.input.clearRetainingCapacity();
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 {
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 {
if (self.input.items.len > 0) {
@ -87,7 +86,7 @@ pub fn Create(options: type) type {
}
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 {
var egc: u32 = 0;
@ -98,7 +97,7 @@ pub fn Create(options: type) type {
try self.input.appendSlice(buf[0..bytes]);
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 {
var bytes: []const u8 = undefined;
@ -107,18 +106,18 @@ pub fn Create(options: type) type {
try self.input.appendSlice(bytes);
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 {
options.select(self);
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 {
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 {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.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.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 {
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 {
self.delete_to_previous_path_segment();
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 {
if (self.file_path.items.len > 0) {
@ -278,25 +277,25 @@ pub fn Create(options: type) type {
}
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 {
self.try_complete_file() catch |e| return tp.exit_error(e, @errorReturnTrace());
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 {
self.complete_trigger_count = 0;
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 {
self.reverse_complete_file() catch |e| return tp.exit_error(e, @errorReturnTrace());
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 {
var egc: u32 = 0;
@ -308,7 +307,7 @@ pub fn Create(options: type) type {
try self.file_path.appendSlice(buf[0..bytes]);
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 {
var bytes: []const u8 = undefined;
@ -318,18 +317,18 @@ pub fn Create(options: type) type {
try self.file_path.appendSlice(bytes);
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 {
options.select(self);
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 {
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);
allocator: Allocator,
input_: ArrayList(u8),
input: ArrayList(u8),
last_input: ArrayList(u8),
start_view: ed.View,
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);
self.* = .{
.allocator = allocator,
.input_ = ArrayList(u8).init(allocator),
.input = ArrayList(u8).init(allocator),
.last_input = ArrayList(u8).init(allocator),
.start_view = editor.view,
.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: {
const text = editor.get_selection(sel, self.allocator) catch break :ret;
defer self.allocator.free(text);
try self.input_.appendSlice(text);
try self.input.appendSlice(text);
}
var mode = try keybind.mode("mini/find", allocator, .{
.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 {
self.commands.deinit();
self.input_.deinit();
self.input.deinit();
self.last_input.deinit();
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 {
var buf: [16]u8 = undefined;
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 {
try self.input_.appendSlice(bytes);
try self.input.appendSlice(bytes);
}
fn flush_input(self: *Self) !void {
if (self.input_.items.len > 0) {
if (eql(u8, self.input_.items, self.last_input.items))
if (self.input.items.len > 0) {
if (eql(u8, self.input.items, self.last_input.items))
return;
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;
const primary = self.editor.get_primary();
primary.selection = null;
primary.cursor = self.start_cursor;
try self.editor.find_in_buffer(self.input_.items);
try self.editor.find_in_buffer(self.input.items);
} else {
self.editor.get_primary().selection = null;
self.editor.init_matches_update();
@ -114,9 +114,9 @@ fn find_history_prev(self: *Self) void {
if (pos > 0) self.history_pos = pos - 1;
} else {
self.history_pos = history.items.len - 1;
if (self.input_.items.len > 0)
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 (self.input.items.len > 0)
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)
self.history_pos = self.history_pos.? - 1;
}
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 {
if (self.editor.find_history) |*history| {
self.input_.clearRetainingCapacity();
self.input_.appendSlice(history.items[pos]) catch {};
self.input.clearRetainingCapacity();
self.input.appendSlice(history.items[pos]) catch {};
}
}
fn update_mini_mode_text(self: *Self) void {
if (tui.mini_mode()) |mini_mode| {
mini_mode.text = self.input_.items;
mini_mode.cursor = tui.egc_chunk_width(self.input_.items, 0, 8);
mini_mode.text = self.input.items;
mini_mode.cursor = tui.egc_chunk_width(self.input.items, 0, 8);
}
}
const cmds = struct {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn mini_mode_reset(self: *Self, _: Ctx) Result {
self.input_.clearRetainingCapacity();
self.input.clearRetainingCapacity();
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 {
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 {
self.editor.push_find_history(self.input_.items);
self.editor.push_find_history(self.input.items);
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 {
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.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 {
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.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 {
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();
}
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 {
self.find_history_prev();
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 {
self.find_history_next();
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 {
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,
buf: [max_query_size]u8 = undefined,
input_: []u8 = "",
input: []u8 = "",
last_buf: [max_query_size]u8 = undefined,
last_input: []u8 = "",
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| {
defer self.allocator.free(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, .{
.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 {
if (self.input_.len + 6 >= self.buf.len)
if (self.input.len + 6 >= self.buf.len)
return;
const bytes = try input.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input_.len..]);
self.input_ = self.buf[0 .. self.input_.len + bytes];
const bytes = try input.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]);
self.input = self.buf[0 .. self.input.len + bytes];
}
fn insert_bytes(self: *Self, bytes_: []const u8) !void {
const bytes = bytes_[0..@min(self.buf.len - self.input_.len, bytes_.len)];
const newlen = self.input_.len + bytes.len;
@memcpy(self.buf[self.input_.len..newlen], bytes);
self.input_ = self.buf[0..newlen];
const bytes = bytes_[0..@min(self.buf.len - self.input.len, bytes_.len)];
const newlen = self.input.len + bytes.len;
@memcpy(self.buf[self.input.len..newlen], bytes);
self.input = self.buf[0..newlen];
}
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;
@memcpy(self.last_buf[0..self.input_.len], self.input_);
self.last_input = self.last_buf[0..self.input_.len];
try command.executeName("find_in_files_query", command.fmt(.{self.input_}));
@memcpy(self.last_buf[0..self.input.len], self.input);
self.last_input = self.last_buf[0..self.input.len];
try command.executeName("find_in_files_query", command.fmt(.{self.input}));
}
fn update_mini_mode_text(self: *Self) void {
if (tui.mini_mode()) |mini_mode| {
mini_mode.text = self.input_;
mini_mode.cursor = tui.egc_chunk_width(self.input_, 0, 8);
mini_mode.text = self.input;
mini_mode.cursor = tui.egc_chunk_width(self.input, 0, 8);
}
}
const cmds = struct {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn mini_mode_reset(self: *Self, _: Ctx) Result {
self.input_ = "";
self.input = "";
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 {
command.executeName("close_find_in_files_results", .{}) 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 {
command.executeName("goto_selected_file", .{}) catch {};
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 {
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.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 {
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.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 {
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();
}
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 {
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 {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn mini_mode_reset(self: *Self, _: Ctx) Result {
self.input = null;
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 {
self.input = null;
@ -97,7 +96,7 @@ const cmds = struct {
self.goto();
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 {
if (self.input) |linenum| {
@ -107,7 +106,7 @@ const cmds = struct {
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 {
var keypress: usize = 0;
@ -120,7 +119,7 @@ const cmds = struct {
self.update_mini_mode_text();
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 {
var bytes: []const u8 = undefined;
@ -130,10 +129,5 @@ const cmds = struct {
self.update_mini_mode_text();
self.goto();
}
pub const mini_mode_insert_bytes_meta: 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} };
pub const mini_mode_insert_bytes_meta = .{ .arguments = &.{.string} };
};

View file

@ -1,4 +1,3 @@
const std = @import("std");
const tp = @import("thespian");
const input = @import("input");
@ -17,7 +16,6 @@ const Commands = command.Collection(cmds);
allocator: Allocator,
key: [6]u8 = undefined,
direction: Direction,
operation_command: []const u8,
operation: Operation,
commands: Commands = undefined,
@ -32,18 +30,14 @@ const Operation = enum {
};
pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tui.MiniMode } {
var operation_command: []const u8 = undefined;
_ = ctx.args.match(.{tp.extract(&operation_command)}) 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;
var direction: Direction = undefined;
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 self: *Self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.direction = direction,
.operation_command = try allocator.dupe(u8, operation_command),
.operation = operation,
.operation = if (select) .select else .move,
};
try self.commands.init(self);
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 {
self.commands.deinit();
self.allocator.free(self.operation_command);
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 {
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", .{});
}
const cmds = struct {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.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;
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 {
var bytes: []const u8 = undefined;
@ -103,10 +105,10 @@ const cmds = struct {
return error.InvalidMoveToCharInsertBytesArgument;
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 {
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 {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn palette_menu_top(self: *Self, _: Ctx) Result {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
var egc: u32 = 0;
@ -331,7 +330,7 @@ const cmds = struct {
return error.InvalidOpenRecentInsertCodePointArgument;
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 {
var bytes: []const u8 = undefined;
@ -339,20 +338,20 @@ const cmds = struct {
return error.InvalidOpenRecentInsertBytesArgument;
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 {
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 {
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 {
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 {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.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.selection_updated();
}
pub const palette_menu_down_meta: Meta = .{};
pub const palette_menu_down_meta = .{};
pub fn palette_menu_up(self: *Self, _: Ctx) Result {
if (self.menu.selected) |selected| {
@ -399,7 +398,7 @@ pub fn Create(options: type) type {
self.menu.select_up();
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 {
if (self.total_items > self.view_rows) {
@ -411,7 +410,7 @@ pub fn Create(options: type) type {
self.menu.select_last();
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 {
if (self.view_pos > self.view_rows)
@ -422,7 +421,7 @@ pub fn Create(options: type) type {
self.menu.select_first();
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 {
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 {
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 {
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 {
if (@hasDecl(options, "cancel")) try options.cancel(self);
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 {
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 {
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 {
var egc: u32 = 0;
@ -471,7 +470,7 @@ pub fn Create(options: type) type {
return error.InvalidPaletteInsertCodePointArgument;
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 {
var bytes: []const u8 = undefined;
@ -479,22 +478,22 @@ pub fn Create(options: type) type {
return error.InvalidPaletteInsertBytesArgument;
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 {
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 {
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 {
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 {
pub const Target = void;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn w(_: *void, _: Ctx) Result {
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 {
try cmd("quit", .{});
}
pub const q_meta: Meta = .{ .description = "q (quit)" };
pub const q_meta = .{ .description = "q (quit)" };
pub fn @"q!"(_: *void, _: Ctx) Result {
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 {
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 {
cmd("save_file", .{}) catch {};
try cmd("quit_without_saving", .{});
}
pub const @"wq!_meta": 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 const @"wq!_meta" = .{ .description = "wq! (write file and quit without saving)" };
pub fn enter_mode_at_next_char(self: *void, ctx: Ctx) Result {
_ = self; // autofix
@ -61,7 +52,7 @@ const cmds_ = struct {
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 {
_ = self; // autofix
@ -70,7 +61,7 @@ const cmds_ = struct {
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 {
_ = self; // autofix
@ -78,7 +69,7 @@ const cmds_ = struct {
//TODO
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 {
_ = self; // autofix
@ -87,7 +78,7 @@ const cmds_ = struct {
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 {
_ = self; // autofix
@ -95,7 +86,7 @@ const cmds_ = struct {
//TODO
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 {
_ = self; // autofix
@ -104,7 +95,7 @@ const cmds_ = struct {
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 {
_ = self; // autofix
@ -119,5 +110,5 @@ const cmds_ = struct {
//try self.update_buf(root);
//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 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 });
if (style == .grip) w.after_render = render_grip;
w.ctx = w;
var it = std.mem.splitScalar(u8, config, ' ');
while (it.next()) |widget_name| {
try w.add(status_widget.create(widget_name, allocator, w.plane, event_handler) catch |e| switch (e) {
error.OutOfMemory => return error.OutOfMemory,
error.WidgetInitFailed => null,
} orelse continue);
}
while (it.next()) |widget_name|
try w.add(try status_widget.create(widget_name, allocator, w.plane, event_handler) orelse continue);
return w.widget();
}

View file

@ -6,24 +6,18 @@ const EventHandler = @import("EventHandler");
const Widget = @import("../Widget.zig");
plane: Plane,
layout_: Widget.Layout,
layout: Widget.Layout,
on_event: ?EventHandler,
const Self = @This();
pub fn Create(comptime layout_: Widget.Layout) @import("widget.zig").CreateFunction {
return struct {
fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) @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_;
fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
const self: *Self = try allocator.create(Self);
self.* = .{
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
.layout_ = layout__,
.layout = layout_,
.on_event = event_handler,
};
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 {
return self.layout_;
return self.layout;
}
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 MessageFilter = @import("../MessageFilter.zig");
const tui = @import("../tui.zig");
const fonts = @import("../fonts.zig");
const DigitStyle = fonts.DigitStyle;
allocator: std.mem.Allocator,
plane: Plane,
tick_timer: ?tp.Cancellable = null,
on_event: ?EventHandler,
tz: zeit.timezone.TimeZone,
style: ?DigitStyle,
const Self = @This();
pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) @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| {
std.log.err("clock: std.process.getEnvMap failed with {any}", .{e});
return error.WidgetInitFailed;
};
pub fn create(allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
var env = std.process.getEnvMap(allocator) catch |e| return tp.exit_error(e, @errorReturnTrace());
defer env.deinit();
const self: *Self = try allocator.create(Self);
self.* = .{
.allocator = allocator,
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),
.on_event = event_handler,
.tz = zeit.local(allocator, &env) catch |e| {
std.log.err("clock: zeit.local failed with {any}", .{e});
return error.WidgetInitFailed;
},
.style = style,
.tz = zeit.local(allocator, &env) catch |e| return tp.exit_error(e, @errorReturnTrace()),
};
try tui.message_filters().add(MessageFilter.bind(self, receive_tick));
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 dt = now.time();
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 {};
_ = self.plane.print("{d:0>2}:{d:0>2}", .{ dt.hour, dt.minute }) catch {};
return false;
}

View file

@ -18,7 +18,7 @@ rendered: [:0]const u8 = "",
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, .{
.ctx = .{},
.label = "",

View file

@ -7,7 +7,6 @@ const root = @import("root");
const Plane = @import("renderer").Plane;
const style = @import("renderer").style;
const styles = @import("renderer").styles;
const command = @import("command");
const EventHandler = @import("EventHandler");
@ -38,7 +37,7 @@ utf8_sanitized: bool = false,
const project_icon = "";
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, .{
.ctx = .{
.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 {
plane.off_styles(styles.italic);
plane.off_styles(style.italic);
const mini_mode = tui.mini_mode() orelse return;
_ = plane.print(" {s}", .{mini_mode.text}) catch {};
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 all
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 {};
if (self.file_icon.len > 0 and tui.config().show_fileicons) {
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 {
plane.on_styles(styles.italic);
plane.on_styles(style.italic);
_ = plane.putstr(" ") catch {};
if (self.file_icon.len > 0 and tui.config().show_fileicons) {
self.render_file_icon(plane, theme);

View file

@ -11,7 +11,7 @@ plane: Plane,
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);
self.* = .{
.allocator = allocator,
@ -29,12 +29,9 @@ pub fn layout(_: *Self) Widget.Layout {
var buf: [256]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
const writer = fbs.writer();
writer.print(" ", .{}) catch {};
if (keybind.current_integer_argument()) |integer_argument|
writer.print("{}", .{integer_argument}) catch {};
writer.print("{} ", .{keybind.current_key_event_sequence_fmt()}) catch {};
writer.print("{}", .{keybind.current_key_event_sequence_fmt()}) catch {};
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 {
@ -44,9 +41,6 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
self.plane.set_style(theme.statusbar);
self.plane.fill(" ");
self.plane.home();
_ = self.plane.print(" ", .{}) catch {};
if (keybind.current_integer_argument()) |integer_argument|
_ = self.plane.print("{}", .{integer_argument}) catch {};
_ = self.plane.print("{} ", .{keybind.current_key_event_sequence_fmt()}) catch {};
_ = self.plane.print(" {} ", .{keybind.current_key_event_sequence_fmt()}) catch {};
return false;
}

View file

@ -29,7 +29,7 @@ const Self = @This();
const idle_msg = "🐶";
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 self: *Self = try allocator.create(Self);
self.* = .{

View file

@ -9,9 +9,6 @@ const EventHandler = @import("EventHandler");
const Widget = @import("../Widget.zig");
const Button = @import("../Button.zig");
const fonts = @import("../fonts.zig");
const DigitStyle = fonts.DigitStyle;
const utf8_sanitized_warning = "  UTF";
@ -22,32 +19,12 @@ buf: [256]u8 = undefined,
rendered: [:0]const u8 = "",
eol_mode: Buffer.EolMode = .lf,
utf8_sanitized: bool = false,
padding: ?usize,
leader: ?Leader,
style: ?DigitStyle,
const Leader = enum {
space,
zero,
};
const Self = @This();
pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) @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 };
pub fn create(allocator: Allocator, parent: Plane, event_handler: ?EventHandler) @import("widget.zig").CreateError!Widget {
return Button.create_widget(Self, allocator, parent, .{
.ctx = .{
.padding = padding,
.leader = leader,
.style = style,
},
.ctx = .{},
.label = "",
.on_click = on_click,
.on_layout = layout,
@ -90,30 +67,11 @@ fn format(self: *Self) void {
.lf => "",
.crlf => " [␍␊]",
};
std.fmt.format(writer, "{s} Ln ", .{eol_mode}) 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 {};
std.fmt.format(writer, "{s} Ln {d}, Col {d} ", .{ eol_mode, self.line + 1, self.column + 1 }) catch {};
self.rendered = @ptrCast(fbs.getWritten());
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 {
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) })) {

View file

@ -26,7 +26,7 @@ const Level = enum {
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);
self.* = .{
.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);
try tui.message_filters().add(MessageFilter.bind(self, receive_log));
log.subscribe() catch return error.WidgetInitFailed;
try log.subscribe();
return Widget.to(self);
}

View file

@ -3,7 +3,6 @@ const Allocator = std.mem.Allocator;
const Plane = @import("renderer").Plane;
const style = @import("renderer").style;
const styles = @import("renderer").styles;
const command = @import("command");
const EventHandler = @import("EventHandler");
@ -12,7 +11,7 @@ const Button = @import("../Button.zig");
const tui = @import("../tui.zig");
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, .{
.ctx = {},
.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.fill(" ");
self.plane.home();
self.plane.on_styles(styles.bold);
self.plane.on_styles(style.bold);
var buf: [31:0]u8 = undefined;
if (!is_mini_mode() and !is_overlay_mode()) {
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.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 {};
if (is_mini_mode())
render_separator(self, theme);

View file

@ -19,7 +19,7 @@ const Self = @This();
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);
self.* = .{
.plane = try Plane.init(&(Widget.Box{}).opts(@typeName(Self)), parent),

View file

@ -18,7 +18,7 @@ on_event: ?EventHandler,
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);
self.* = .{
.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 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);
self.* = try TabBar.init(allocator, parent, event_handler);
return Widget.to(self);
@ -428,7 +428,7 @@ const Tab = struct {
const spacer = struct {
plane: Plane,
layout_: Widget.Layout,
layout: Widget.Layout,
on_event: ?EventHandler,
content: []const u8,
fg: colors,
@ -447,7 +447,7 @@ const spacer = struct {
const self: *Self = try allocator.create(Self);
self.* = .{
.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,
.content = content,
.fg = fg,
@ -462,7 +462,7 @@ const spacer = struct {
}
pub fn layout(self: *Self) Widget.Layout {
return self.layout_;
return self.layout;
}
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 },
.{ "keybind", @import("keybindstate.zig").create },
.{ "tabs", @import("tabs.zig").create },
.{ "branch", @import("branch.zig").create },
});
pub const CreateError = error{ OutOfMemory, WidgetInitFailed };
pub const CreateFunction = *const fn (allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler, arg: ?[]const u8) 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 const CreateError = error{ OutOfMemory, Exit };
pub const CreateFunction = *const fn (allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!Widget;
pub fn create(name: []const u8, allocator: std.mem.Allocator, parent: Plane, event_handler: ?EventHandler) CreateError!?Widget {
const create_ = widgets.get(name) orelse {
const logger = log.logger("statusbar");
logger.print_err("config", "unknown widget \"{s}\" (see log)", .{name});
log_widgets(logger);
return null;
};
return try create_(allocator, parent, event_handler, arg);
return try create_(allocator, parent, event_handler);
}
fn log_widgets(logger: anytype) void {

View file

@ -12,7 +12,6 @@ pub const renderer = @import("renderer");
const command = @import("command");
const EventHandler = @import("EventHandler");
const keybind = @import("keybind");
const syntax = @import("syntax");
const Widget = @import("Widget.zig");
const MessageFilter = @import("MessageFilter.zig");
@ -21,31 +20,29 @@ const MainView = @import("mainview.zig");
const Allocator = std.mem.Allocator;
allocator: Allocator,
rdr_: renderer,
config_: @import("config"),
highlight_columns_: []u16,
rdr: renderer,
config: @import("config"),
frame_time: usize, // in microseconds
frame_clock: tp.metronome,
frame_clock_running: bool = false,
frame_last_time: i64 = 0,
receiver: Receiver,
mainview_: ?Widget = null,
message_filters_: MessageFilter.List,
input_mode_: ?Mode = null,
mainview: ?Widget = null,
message_filters: MessageFilter.List,
input_mode: ?Mode = null,
delayed_init_done: bool = false,
delayed_init_input_mode: ?Mode = null,
input_mode_outer_: ?Mode = null,
input_listeners_: EventHandler.List,
input_mode_outer: ?Mode = null,
input_listeners: EventHandler.List,
keyboard_focus: ?Widget = null,
mini_mode_: ?MiniMode = null,
mini_mode: ?MiniMode = null,
hover_focus: ?*Widget = null,
last_hover_x: c_int = -1,
last_hover_y: c_int = -1,
commands: Commands = undefined,
logger: log.Logger,
drag_source: ?*Widget = null,
theme_: Widget.Theme,
parsed_theme: ?std.json.Parsed(Widget.Theme),
theme: Widget.Theme,
idle_frame_count: usize = 0,
unrendered_input_events_count: usize = 0,
init_timer: ?tp.timeout,
@ -56,10 +53,9 @@ render_pending: bool = false,
keepalive_timer: ?tp.Cancellable = null,
mouse_idle_timer: ?tp.Cancellable = null,
default_cursor: keybind.CursorShape = .default,
fontface_: []const u8 = "",
fontfaces_: std.ArrayListUnmanaged([]const u8) = .{},
fontface: []const u8 = "",
fontfaces: std.ArrayListUnmanaged([]const u8) = .{},
enable_mouse_idle_timer: bool = false,
query_cache_: *syntax.QueryCache,
const keepalive = std.time.us_per_day * 365; // one year
const idle_frames = 0;
@ -86,96 +82,61 @@ fn start(args: StartArgs) tp.result {
tp.receive(&self.receiver);
}
const InitError = error{
OutOfMemory,
UnknownTheme,
ThespianMetronomeInitFailed,
ThespianMetronomeStartFailed,
ThespianTimeoutInitFailed,
ThespianSignalInitFailed,
ThespianSpawnFailed,
} || renderer.Error ||
root.ConfigDirError ||
root.ConfigWriteError ||
keybind.LoadError;
fn init(allocator: Allocator) InitError!*Self {
fn init(allocator: Allocator) !*Self {
var conf, const conf_bufs = root.read_config(@import("config"), allocator);
defer root.free_config(allocator, conf_bufs);
if (conf.start_debugger_on_crash)
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;
const theme_ = get_theme_by_name(conf.theme) orelse get_theme_by_name("dark_modern") orelse return tp.exit("unknown theme");
conf.theme = theme_.name;
conf.whitespace_mode = try allocator.dupe(u8, conf.whitespace_mode);
conf.input_mode = try allocator.dupe(u8, conf.input_mode);
conf.top_bar = try allocator.dupe(u8, conf.top_bar);
conf.bottom_bar = try allocator.dupe(u8, conf.bottom_bar);
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;
const frame_rate: usize = @intCast(tp.env.get().num("frame-rate"));
if (frame_rate != 0)
conf.frame_rate = 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_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);
self.* = .{
.allocator = allocator,
.config_ = conf,
.highlight_columns_ = try allocator.alloc(u16, hl_cols),
.rdr_ = try renderer.init(allocator, self, tp.env.get().is("no-alternate"), dispatch_initialized),
.config = conf,
.rdr = try renderer.init(allocator, self, tp.env.get().is("no-alternate"), dispatch_initialized),
.frame_time = frame_time,
.frame_clock = frame_clock,
.frame_clock_running = true,
.receiver = Receiver.init(receive, self),
.message_filters_ = MessageFilter.List.init(allocator),
.input_listeners_ = EventHandler.List.init(allocator),
.message_filters = MessageFilter.List.init(allocator),
.input_listeners = EventHandler.List.init(allocator),
.logger = log.logger("tui"),
.init_timer = if (build_options.gui) null else try tp.timeout.init_ms(init_delay, tp.message.fmt(
.{"init"},
)),
.theme_ = theme_,
.theme = theme_,
.no_sleep = tp.env.get().is("no-sleep"),
.query_cache_ = try syntax.QueryCache.create(allocator, .{}),
.parsed_theme = parsed_theme,
};
instance_ = self;
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.config_.default_cursor = @tagName(self.default_cursor);
self.rdr_.handler_ctx = self;
self.rdr_.dispatch_input = dispatch_input;
self.rdr_.dispatch_mouse = dispatch_mouse;
self.rdr_.dispatch_mouse_drag = dispatch_mouse_drag;
self.rdr_.dispatch_event = dispatch_event;
try self.rdr_.run();
self.config.default_cursor = @tagName(self.default_cursor);
self.rdr.handler_ctx = self;
self.rdr.dispatch_input = dispatch_input;
self.rdr.dispatch_mouse = dispatch_mouse;
self.rdr.dispatch_mouse_drag = dispatch_mouse_drag;
self.rdr.dispatch_event = dispatch_event;
try self.rdr.run();
try project_manager.start();
try frame_clock.start();
try self.commands.init(self);
try keybind.init();
errdefer self.deinit();
switch (builtin.os.tag) {
.windows => {
@ -185,7 +146,7 @@ fn init(allocator: Allocator) InitError!*Self {
try self.listen_sigwinch();
},
}
self.mainview_ = try MainView.create(allocator);
self.mainview = try MainView.create(allocator);
resize();
self.set_terminal_style();
try save_config();
@ -197,20 +158,20 @@ fn init(allocator: Allocator) InitError!*Self {
return self;
}
fn init_input_namespace(self: *Self) InitError!void {
var mode_parts = std.mem.splitScalar(u8, self.config_.input_mode, '/');
fn init_input_namespace(self: *Self) !void {
var mode_parts = std.mem.splitScalar(u8, self.config.input_mode, '/');
const namespace_name = mode_parts.first();
keybind.set_namespace(namespace_name) catch {
self.logger.print_err("keybind", "unknown mode {s}", .{namespace_name});
try keybind.set_namespace("flow");
self.config_.input_mode = "flow";
self.config.input_mode = "flow";
try save_config();
};
}
fn init_delayed(self: *Self) command.Result {
fn init_delayed(self: *Self) !void {
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| {
try enter_input_mode(self, delayed_init_input_mode);
self.delayed_init_input_mode = null;
@ -231,32 +192,31 @@ fn deinit(self: *Self) void {
t.deinit();
self.keepalive_timer = null;
}
if (self.input_mode_) |*m| {
if (self.input_mode) |*m| {
m.deinit();
self.input_mode_ = null;
self.input_mode = null;
}
if (self.delayed_init_input_mode) |*m| {
m.deinit();
self.delayed_init_input_mode = null;
}
self.commands.deinit();
if (self.mainview_) |*mv| mv.deinit(self.allocator);
self.message_filters_.deinit();
self.input_listeners_.deinit();
if (self.mainview) |*mv| mv.deinit(self.allocator);
self.message_filters.deinit();
self.input_listeners.deinit();
if (self.frame_clock_running)
self.frame_clock.stop() catch {};
if (self.sigwinch_signal) |sig| sig.deinit();
self.frame_clock.deinit();
self.rdr_.stop();
self.rdr_.deinit();
self.rdr.stop();
self.rdr.deinit();
self.logger.deinit();
self.query_cache_.deinit();
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();
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 {
@ -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 {
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),
else => return e,
};
@ -303,7 +263,7 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
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;
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 (try m.match(.{"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;
};
@ -430,14 +390,14 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void {
var fontface_: []const u8 = undefined;
if (try m.match(.{ "fontface", "current", tp.extract(&fontface_) })) {
if (self.fontface_.len > 0) self.allocator.free(self.fontface_);
self.fontface_ = "";
self.fontface_ = try self.allocator.dupe(u8, fontface_);
if (self.fontface.len > 0) self.allocator.free(self.fontface);
self.fontface = "";
self.fontface = try self.allocator.dupe(u8, fontface_);
return;
}
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;
}
@ -460,20 +420,20 @@ fn render(self: *Self) void {
{
const frame = tracy.initZone(@src(), .{ .name = "tui update" });
defer frame.deinit();
if (self.mainview_) |mv| mv.update();
if (self.mainview) |mv| mv.update();
}
const more = ret: {
const frame = tracy.initZone(@src(), .{ .name = "tui render" });
defer frame.deinit();
self.rdr_.stdplane().erase();
break :ret if (self.mainview_) |mv| mv.render(&self.theme_) else false;
self.rdr.stdplane().erase();
break :ret if (self.mainview) |mv| mv.render(&self.theme) else false;
};
{
const frame = tracy.initZone(@src(), .{ .name = renderer.log_name ++ " render" });
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();
}
@ -497,13 +457,13 @@ fn render(self: *Self) void {
}
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;
}
fn dispatch_flush_input_event(self: *Self) error{ Exit, NoSpaceLeft }!void {
fn dispatch_flush_input_event(self: *Self) !void {
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"}));
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 from = tp.self_pid();
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 (w.send(from, m) catch |e| ret: {
self.logger.err("focus", e);
break :ret false;
})
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);
}
@ -575,7 +535,7 @@ fn find_coord_widget(self: *Self, y: usize, x: usize) ?*Widget {
}
};
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;
}
@ -592,7 +552,7 @@ fn is_live_widget_ptr(self: *Self, w_: *Widget) bool {
}
};
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 {
@ -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);
return if (self.keyboard_focus) |w|
w.send(from, m)
else if (self.mainview_) |mv|
else if (self.mainview) |mv|
mv.send(from, m)
else
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 {
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| {
_ = try w.send(from, m);
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 {
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| {
_ = try w.send(from, m);
return;
@ -663,24 +623,23 @@ pub fn refresh_hover() void {
_ = 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();
try root.write_config(self.config_, self.allocator);
try root.write_config(self.config, self.allocator);
}
pub fn is_mainview_focused() bool {
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 {
command.executeName("disable_fast_scroll", .{}) catch {};
command.executeName("disable_jump_mode", .{}) catch {};
if (self.mini_mode_) |_| try cmds.exit_mini_mode(self, .{});
if (self.input_mode_outer_) |_| try cmds.exit_overlay_mode(self, .{});
self.input_mode_outer_ = self.input_mode_;
self.input_mode_ = try mode.create(self.allocator);
if (self.input_mode_) |*m| m.run_init();
if (self.mini_mode) |_| try cmds.exit_mini_mode(self, .{});
if (self.input_mode_outer) |_| try cmds.exit_overlay_mode(self, .{});
self.input_mode_outer = self.input_mode;
self.input_mode = try mode.create(self.allocator);
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 {
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_) |*m| {
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) |*m| {
m.deinit();
self.input_mode_ = null;
self.input_mode = null;
}
self.input_mode_ = new_mode;
if (self.input_mode_) |*m| m.run_init();
self.input_mode = new_mode;
}
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: {
self.logger.print("unknown mode {s}", .{mode});
break :ret try self.get_input_mode(keybind.default_mode);
};
errdefer new_mode.deinit();
if (self.input_mode_) |*m| {
if (self.input_mode) |*m| {
m.deinit();
self.input_mode_ = null;
self.input_mode = null;
}
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();
self.input_mode = new_mode;
}
pub const enter_mode_meta = .{ .arguments = &.{.string} };
const cmds = struct {
pub const Target = Self;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
const Meta = command.Metadata;
pub fn restart(_: *Self, _: Ctx) Result {
try tp.self_pid().send("restart");
@ -743,54 +688,67 @@ const cmds = struct {
root.print_exit_status({}, "FORCE TERMINATE");
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 {
var name: []const u8 = undefined;
if (!try ctx.args.match(.{tp.extract(&name)}))
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 {
const name = get_next_theme_by_name(self.theme_.name);
return self.set_theme_by_name(name);
self.theme = get_next_theme_by_name(self.theme.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 fn theme_prev(self: *Self, _: Ctx) Result {
const name = get_prev_theme_by_name(self.theme_.name);
return self.set_theme_by_name(name);
self.theme = get_prev_theme_by_name(self.theme.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 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"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "indent"))
else if (std.mem.eql(u8, self.config.whitespace_mode, "indent"))
"leading"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "leading"))
else if (std.mem.eql(u8, self.config.whitespace_mode, "leading"))
"eol"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "eol"))
else if (std.mem.eql(u8, self.config.whitespace_mode, "eol"))
"tabs"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "tabs"))
else if (std.mem.eql(u8, self.config.whitespace_mode, "tabs"))
"visible"
else if (std.mem.eql(u8, self.config_.whitespace_mode, "visible"))
else if (std.mem.eql(u8, self.config.whitespace_mode, "visible"))
"full"
else
"none";
try save_config();
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);
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 fn toggle_input_mode(self: *Self, _: Ctx) Result {
var it = std.mem.splitScalar(u8, self.config_.input_mode, '/');
self.config_.input_mode = it.first();
var it = std.mem.splitScalar(u8, self.config.input_mode, '/');
self.config.input_mode = it.first();
const namespaces = keybind.get_namespaces(self.allocator) catch |e| return tp.exit_error(e, @errorReturnTrace());
defer {
@ -798,15 +756,15 @@ const cmds = struct {
self.allocator.free(namespaces);
}
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 (std.mem.eql(u8, namespace, self.config_.input_mode))
if (std.mem.eql(u8, namespace, self.config.input_mode))
found = true;
} else try self.allocator.dupe(u8, namespaces[0]);
try save_config();
self.logger.print("input mode {s}", .{self.config_.input_mode});
try keybind.set_namespace(self.config_.input_mode);
self.logger.print("input mode {s}", .{self.config.input_mode});
try keybind.set_namespace(self.config.input_mode);
return self.refresh_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);
}
pub const enter_mode_meta: Meta = .{ .arguments = &.{.string} };
pub const enter_mode_meta = .{ .arguments = &.{.string} };
pub fn enter_mode_default(self: *Self, _: Ctx) Result {
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 {
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 {
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 {
return self.enter_overlay_mode(@import("mode/overlay/task_palette.zig").Type);
@ -896,7 +854,7 @@ const cmds = struct {
return error.InvalidDeleteTaskArgument;
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 {
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 {
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 {
if (build_options.gui)
self.rdr_.get_fontfaces();
self.rdr.get_fontfaces();
}
pub const change_fontface_meta: Meta = .{ .description = "Change font" };
pub fn exit_overlay_mode(self: *Self, _: Ctx) Result {
self.rdr_.cursor_disable();
if (self.input_mode_outer_ == null) return enter_mode_default(self, .{});
if (self.input_mode_) |*mode| mode.deinit();
self.input_mode_ = self.input_mode_outer_;
self.input_mode_outer_ = null;
self.rdr.cursor_disable();
if (self.input_mode_outer == null) return enter_mode_default(self, .{});
if (self.input_mode) |*mode| mode.deinit();
self.input_mode = self.input_mode_outer;
self.input_mode_outer = null;
refresh_hover();
}
pub const exit_overlay_mode_meta: Meta = .{};
pub const exit_overlay_mode_meta = .{};
pub fn find(self: *Self, ctx: Ctx) Result {
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 {
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 {
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);
}
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 {
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 {
command.executeName("disable_fast_scroll", .{}) catch {};
command.executeName("disable_jump_mode", .{}) catch {};
const input_mode_, const mini_mode_ = try mode.create(self.allocator, ctx);
if (self.mini_mode_) |_| try exit_mini_mode(self, .{});
if (self.input_mode_outer_) |_| try exit_overlay_mode(self, .{});
if (self.input_mode_outer_ != null) @panic("exit_overlay_mode failed");
self.input_mode_outer_ = self.input_mode_;
self.input_mode_ = input_mode_;
self.mini_mode_ = mini_mode_;
if (self.input_mode_) |*m| m.run_init();
if (self.mini_mode) |_| try exit_mini_mode(self, .{});
if (self.input_mode_outer) |_| try exit_overlay_mode(self, .{});
if (self.input_mode_outer != null) @panic("exit_overlay_mode failed");
self.input_mode_outer = self.input_mode;
self.input_mode = input_mode_;
self.mini_mode = mini_mode_;
}
pub fn exit_mini_mode(self: *Self, _: Ctx) Result {
self.rdr_.cursor_disable();
if (self.mini_mode_) |_| {} else return;
if (self.input_mode_) |*mode| mode.deinit();
self.input_mode_ = self.input_mode_outer_;
self.input_mode_outer_ = null;
self.mini_mode_ = null;
self.rdr.cursor_disable();
if (self.mini_mode) |_| {} else return;
if (self.input_mode) |*mode| mode.deinit();
self.input_mode = self.input_mode_outer;
self.input_mode_outer = 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 {
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 file_name = try keybind.get_or_create_namespace_config_file(self.allocator, namespace_name);
try tp.self_pid().send(.{ "cmd", "navigate", .{ .file = file_name } });
self.logger.print("restart flow to use changed key bindings", .{});
}
pub const open_keybind_config_meta: 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 const open_keybind_config_meta = .{ .description = "Edit key bindings" };
pub fn run_async(self: *Self, ctx: Ctx) Result {
var iter = ctx.args.buf;
@ -1041,27 +991,27 @@ const cmds = struct {
}
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 {
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 {
@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 {
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 {
@import("mode/helix.zig").deinit();
}
pub const exit_helix_mode_meta: Meta = .{};
pub const exit_helix_mode_meta = .{};
};
pub const MiniMode = struct {
@ -1080,51 +1030,43 @@ fn current() *Self {
}
pub fn rdr() *renderer {
return &current().rdr_;
return &current().rdr;
}
pub fn message_filters() *MessageFilter.List {
return &current().message_filters_;
return &current().message_filters;
}
pub fn input_listeners() *EventHandler.List {
return &current().input_listeners_;
return &current().input_listeners;
}
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 {
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 {
return if (current().mini_mode_) |*p| p else null;
}
pub fn query_cache() *syntax.QueryCache {
return current().query_cache_;
return if (current().mini_mode) |*p| p else null;
}
pub fn config() *const @import("config") {
return &current().config_;
}
pub fn highlight_columns() []const u16 {
return current().highlight_columns_;
return &current().config;
}
pub fn config_mut() *@import("config") {
return &current().config_;
return &current().config;
}
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 {
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 {
@ -1148,9 +1090,9 @@ fn context_check() void {
}
pub fn get_mode() []const u8 {
return if (current().mini_mode_) |m|
return if (current().mini_mode) |m|
m.name
else if (current().input_mode_) |m|
else if (current().input_mode) |m|
m.name
else
"INI";
@ -1158,7 +1100,7 @@ pub fn get_mode() []const u8 {
pub fn get_keybind_mode() ?Mode {
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 {
@ -1181,11 +1123,11 @@ pub fn resize() void {
}
pub fn plane() renderer.Plane {
return current().rdr_.stdplane();
return current().rdr.stdplane();
}
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 {
@ -1201,49 +1143,44 @@ pub fn screen() Widget.Box {
}
pub fn fontface() []const u8 {
return current().fontface_;
return current().fontface;
}
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 {
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) } {
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 };
}
pub fn get_theme_by_name(name: []const u8) ?Widget.Theme {
for (Widget.themes) |theme_| {
if (std.mem.eql(u8, theme_.name, name))
return .{ theme_, null };
return theme_;
}
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;
for (Widget.themes) |theme_| {
if (next)
return theme_.name;
return theme_;
if (std.mem.eql(u8, theme_.name, name))
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;
for (Widget.themes) |theme_| {
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_;
}
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 {
@ -1332,15 +1269,15 @@ pub const fallbacks: []const FallBack = &[_]FallBack{
};
fn set_terminal_style(self: *Self) void {
if (build_options.gui or self.config_.enable_terminal_color_scheme) {
self.rdr_.set_terminal_style(self.theme_.editor);
self.rdr_.set_terminal_cursor_color(self.theme_.editor_cursor.bg.?);
if (build_options.gui or self.config.enable_terminal_color_scheme) {
self.rdr.set_terminal_style(self.theme.editor);
self.rdr.set_terminal_cursor_color(self.theme.editor_cursor.bg.?);
}
}
pub fn get_cursor_shape() renderer.CursorShape {
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) {
.default => .default,
.block_blink => .block_blink,
@ -1360,7 +1297,7 @@ pub fn is_cursor_beam() bool {
}
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 {
@ -1386,32 +1323,3 @@ pub fn render_match_cell(self: *renderer.Plane, y: usize, x: usize, theme_: *con
cell.set_style(theme_.editor_match);
_ = 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(
"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();
const cell_size: XY(u16) = blk: {
const cell_size = blk: {
var text_layout: *win32.IDWriteTextLayout = undefined;
{
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 {
return @alignCast(@ptrCast(field.default_value_ptr orelse return null));
return @alignCast(@ptrCast(field.default_value orelse return null));
}
fn getDefaultFontFace() FontFace {