From 1cd3cb17ce0ba2852e9c23d68f593cd16e081928 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 1 May 2024 22:03:33 +0200 Subject: [PATCH] feat: add libvaxis renderer --- build.zig | 24 +- build.zig.zon | 4 + src/buffer/Buffer.zig | 177 ++++----- src/buffer/Cursor.zig | 95 ++--- src/buffer/Selection.zig | 7 +- src/renderer/notcurses/Plane.zig | 40 +- src/renderer/notcurses/egc.zig | 29 -- src/renderer/notcurses/input.zig | 1 + src/renderer/notcurses/renderer.zig | 27 +- src/renderer/vaxis/Cell.zig | 51 +++ src/renderer/vaxis/Plane.zig | 346 ++++++++++++++++++ src/renderer/vaxis/input.zig | 267 ++++++++++++++ src/renderer/vaxis/renderer.zig | 312 ++++++++++++++++ src/renderer/vaxis/style.zig | 14 + src/tui/WidgetList.zig | 6 +- src/tui/editor.zig | 529 ++++++++++++++------------- src/tui/fonts.zig | 25 +- src/tui/home.zig | 30 +- src/tui/inspector_view.zig | 6 +- src/tui/mode/input/flow.zig | 4 +- src/tui/mode/input/vim/insert.zig | 4 +- src/tui/mode/input/vim/normal.zig | 4 +- src/tui/mode/input/vim/visual.zig | 4 +- src/tui/mode/mini/find.zig | 4 +- src/tui/mode/mini/find_in_files.zig | 4 +- src/tui/mode/mini/move_to_char.zig | 4 +- src/tui/mode/mini/open_file.zig | 4 +- src/tui/mode/overlay/open_recent.zig | 8 +- src/tui/scrollbar_v.zig | 4 +- src/tui/status/keystate.zig | 4 +- src/tui/status/modestate.zig | 5 +- src/tui/tui.zig | 32 +- 32 files changed, 1559 insertions(+), 516 deletions(-) delete mode 100644 src/renderer/notcurses/egc.zig create mode 100644 src/renderer/vaxis/Cell.zig create mode 100644 src/renderer/vaxis/Plane.zig create mode 100644 src/renderer/vaxis/input.zig create mode 100644 src/renderer/vaxis/renderer.zig create mode 100644 src/renderer/vaxis/style.zig diff --git a/build.zig b/build.zig index f5c56e2..6dc1562 100644 --- a/build.zig +++ b/build.zig @@ -22,6 +22,7 @@ pub fn build(b: *std.Build) void { const use_llvm_option = b.option(bool, "use_llvm", "Enable llvm backend (default: yes)"); const use_lld_option = b.option(bool, "use_lld", "Enable lld backend (default: yes)"); const use_system_notcurses = b.option(bool, "use_system_notcurses", "Build against system notcurses (default: no)") orelse false; + const use_vaxis = b.option(bool, "use_vaxis", "Enable libvaxis rendering backend (default: no)") orelse false; const tracy_enabled = if (enable_tracy_option) |enabled| enabled else false; const optimize_deps_enabled = if (optimize_deps_option) |enabled| enabled else true; @@ -32,6 +33,7 @@ pub fn build(b: *std.Build) void { options.addOption(bool, "use_llvm", use_llvm_option orelse false); options.addOption(bool, "use_lld", use_lld_option orelse false); options.addOption(bool, "use_system_notcurses", use_system_notcurses); + options.addOption(bool, "use_vaxis", use_vaxis); const options_mod = options.createModule(); @@ -57,6 +59,12 @@ pub fn build(b: *std.Build) void { }); const notcurses_mod = notcurses_dep.module("notcurses"); + const vaxis_dep = b.dependency("vaxis", .{ + .target = target, + .optimize = dependency_optimize, + }); + const vaxis_mod = vaxis_dep.module("vaxis"); + const clap_dep = b.dependency("clap", .{ .target = target, .optimize = dependency_optimize, @@ -128,7 +136,17 @@ pub fn build(b: *std.Build) void { }, }); - const renderer_mod = notcurses_renderer_mod; + const vaxis_renderer_mod = b.createModule(.{ + .root_source_file = .{ .path = "src/renderer/vaxis/renderer.zig" }, + .imports = &.{ + .{ .name = "vaxis", .module = vaxis_mod }, + .{ .name = "theme", .module = themes_dep.module("theme") }, + .{ .name = "cbor", .module = cbor_mod }, + .{ .name = "log", .module = log_mod }, + }, + }); + + const renderer_mod = if (use_vaxis) vaxis_renderer_mod else notcurses_renderer_mod; const Buffer_mod = b.createModule(.{ .root_source_file = .{ .path = "src/buffer/Buffer.zig" }, @@ -210,7 +228,7 @@ pub fn build(b: *std.Build) void { }); const exe = b.addExecutable(.{ - .name = "flow", + .name = if (use_vaxis) "flow-vaxis" else "flow", .root_source_file = .{ .path = "src/main.zig" }, .target = target, .optimize = optimize, @@ -238,7 +256,7 @@ pub fn build(b: *std.Build) void { run_step.dependOn(&run_cmd.step); const check_exe = b.addExecutable(.{ - .name = "flow", + .name = if (use_vaxis) "flow-vaxis" else "flow", .root_source_file = .{ .path = "src/main.zig" }, .target = target, .optimize = optimize, diff --git a/build.zig.zon b/build.zig.zon index 5c8e340..feab8d5 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -35,6 +35,10 @@ .url = "https://github.com/fjebaker/fuzzig/archive/c6a0e0ca1a24e55ebdce51c83b918d4325ca7032.tar.gz", .hash = "1220214dfb9a0806d9c8a059beb9e3b07811fd138cd5baeb9d1da432588920a084bf", }, + .vaxis = .{ + .url = "https://github.com/rockorager/libvaxis/archive/972129a764910c033042cfc50732002af62a0f83.tar.gz", + .hash = "1220b34a066de45d8e58d3d85173bcd24edbd9e87b93791e621acc696d97e8e45c4c", + }, }, .paths = .{ "include", diff --git a/src/buffer/Buffer.zig b/src/buffer/Buffer.zig index 07e68a5..f879145 100644 --- a/src/buffer/Buffer.zig +++ b/src/buffer/Buffer.zig @@ -1,12 +1,10 @@ const std = @import("std"); +const Plane = @import("renderer").Plane; const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; const cwd = std.fs.cwd; -const egc_length = @import("renderer").egc.length; -const egc_chunk_width = @import("renderer").egc.chunk_width; - const Self = @This(); const default_leaf_capacity = 64; @@ -53,7 +51,7 @@ pub const WalkerMut = struct { pub const stop = WalkerMut{ .keep_walking = false }; pub const found = WalkerMut{ .found = true }; - const F = *const fn (ctx: *anyopaque, leaf: *const Leaf) WalkerMut; + const F = *const fn (ctx: *anyopaque, leaf: *const Leaf, plane: Plane) WalkerMut; }; pub const Walker = struct { @@ -65,7 +63,7 @@ pub const Walker = struct { pub const stop = Walker{ .keep_walking = false }; pub const found = Walker{ .found = true }; - const F = *const fn (ctx: *anyopaque, leaf: *const Leaf) Walker; + const F = *const fn (ctx: *anyopaque, leaf: *const Leaf, plane: Plane) Walker; }; pub const Weights = struct { @@ -147,7 +145,7 @@ pub const Leaf = struct { return self.buf.len == 0 and !self.bol and !self.eol; } - fn pos_to_width(self: *const Leaf, pos: *usize, abs_col_: usize) usize { + fn pos_to_width(self: *const Leaf, pos: *usize, abs_col_: usize, plane: Plane) usize { var col: usize = 0; var abs_col = abs_col_; var cols: c_int = 0; @@ -158,7 +156,7 @@ pub const Leaf = struct { buf = buf[1..]; pos.* -= 1; } else { - const bytes = egc_length(buf, &cols, abs_col); + const bytes = plane.egc_length(buf, &cols, abs_col); buf = buf[bytes..]; pos.* -= bytes; } @@ -168,12 +166,12 @@ pub const Leaf = struct { return col; } - fn width(self: *const Leaf, abs_col: usize) usize { + fn width(self: *const Leaf, abs_col: usize, plane: Plane) usize { var pos: usize = std.math.maxInt(usize); - return self.pos_to_width(&pos, abs_col); + return self.pos_to_width(&pos, abs_col, plane); } - inline fn width_to_pos(self: *const Leaf, col_: usize, abs_col_: usize) !usize { + inline fn width_to_pos(self: *const Leaf, col_: usize, abs_col_: usize, plane: Plane) !usize { var abs_col = abs_col_; var col = col_; var cols: c_int = 0; @@ -181,7 +179,7 @@ pub const Leaf = struct { return while (buf.len > 0) { if (col == 0) break @intFromPtr(buf.ptr) - @intFromPtr(self.buf.ptr); - const bytes = egc_length(buf, &cols, abs_col); + const bytes = plane.egc_length(buf, &cols, abs_col); buf = buf[bytes..]; if (col < cols) break @intFromPtr(buf.ptr) - @intFromPtr(self.buf.ptr); @@ -190,20 +188,20 @@ pub const Leaf = struct { } else error.BufferUnderrun; } - inline fn dump(self: *const Leaf, l: *ArrayList(u8), abs_col: usize) !void { + inline fn dump(self: *const Leaf, l: *ArrayList(u8), abs_col: usize, plane: Plane) !void { var buf: [16]u8 = undefined; - const wcwidth = try std.fmt.bufPrint(&buf, "{d}", .{self.width(abs_col)}); + const wcwidth = try std.fmt.bufPrint(&buf, "{d}", .{self.width(abs_col, plane)}); if (self.bol) try l.appendSlice("BOL "); try l.appendSlice(wcwidth); try l.append('"'); - try debug_render_chunk(self.buf, l); + try debug_render_chunk(self.buf, l, plane); try l.appendSlice("\" "); if (self.eol) try l.appendSlice("EOL "); } - fn debug_render_chunk(chunk: []const u8, l: *ArrayList(u8)) !void { + fn debug_render_chunk(chunk: []const u8, l: *ArrayList(u8), plane: Plane) !void { var cols: c_int = 0; var buf = chunk; while (buf.len > 0) { @@ -214,7 +212,7 @@ pub const Leaf = struct { buf = buf[1..]; }, else => { - const bytes = egc_length(buf, &cols, 0); + const bytes = plane.egc_length(buf, &cols, 0); var buf_: [4096]u8 = undefined; try l.appendSlice(try std.fmt.bufPrint(&buf_, "{s}", .{std.fmt.fmtSliceEscapeLower(buf[0..bytes])})); buf = buf[bytes..]; @@ -316,27 +314,27 @@ const Node = union(enum) { return leaves.toOwnedSlice(); } - fn walk_const(self: *const Node, f: Walker.F, ctx: *anyopaque) Walker { + fn walk_const(self: *const Node, f: Walker.F, ctx: *anyopaque, plane: Plane) Walker { switch (self.*) { .node => |*node| { - const left = node.left.walk_const(f, ctx); + const left = node.left.walk_const(f, ctx, plane); if (!left.keep_walking) { var result = Walker{}; result.err = left.err; result.found = left.found; return result; } - const right = node.right.walk_const(f, ctx); + const right = node.right.walk_const(f, ctx, plane); return node.merge_results_const(left, right); }, - .leaf => |*l| return f(ctx, l), + .leaf => |*l| return f(ctx, l, plane), } } - fn walk(self: *const Node, a: Allocator, f: WalkerMut.F, ctx: *anyopaque) WalkerMut { + fn walk(self: *const Node, a: Allocator, f: WalkerMut.F, ctx: *anyopaque, plane: Plane) WalkerMut { switch (self.*) { .node => |*node| { - const left = node.left.walk(a, f, ctx); + const left = node.left.walk(a, f, ctx, plane); if (!left.keep_walking) { var result = WalkerMut{}; result.err = left.err; @@ -346,26 +344,26 @@ const Node = union(enum) { } return result; } - const right = node.right.walk(a, f, ctx); + const right = node.right.walk(a, f, ctx, plane); return node.merge_results(a, left, right); }, - .leaf => |*l| return f(ctx, l), + .leaf => |*l| return f(ctx, l, plane), } } - fn walk_from_line_begin_const_internal(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque) Walker { + fn walk_from_line_begin_const_internal(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque, plane: Plane) Walker { switch (self.*) { .node => |*node| { const left_bols = node.weights.bols; if (line >= left_bols) - return node.right.walk_from_line_begin_const_internal(line - left_bols, f, ctx); - const left_result = node.left.walk_from_line_begin_const_internal(line, f, ctx); - const right_result = if (left_result.found and left_result.keep_walking) node.right.walk_const(f, ctx) else Walker{}; + return node.right.walk_from_line_begin_const_internal(line - left_bols, f, ctx, plane); + const left_result = node.left.walk_from_line_begin_const_internal(line, f, ctx, plane); + const right_result = if (left_result.found and left_result.keep_walking) node.right.walk_const(f, ctx, plane) else Walker{}; return node.merge_results_const(left_result, right_result); }, .leaf => |*l| { if (line == 0) { - var result = f(ctx, l); + var result = f(ctx, l, plane); if (result.err) |_| return result; result.found = true; return result; @@ -375,18 +373,18 @@ const Node = union(enum) { } } - pub fn walk_from_line_begin_const(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque) !bool { - const result = self.walk_from_line_begin_const_internal(line, f, ctx); + pub fn walk_from_line_begin_const(self: *const Node, line: usize, f: Walker.F, ctx: *anyopaque, plane: Plane) !bool { + const result = self.walk_from_line_begin_const_internal(line, f, ctx, plane); if (result.err) |e| return e; return result.found; } - fn walk_from_line_begin_internal(self: *const Node, a: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque) WalkerMut { + fn walk_from_line_begin_internal(self: *const Node, a: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque, plane: Plane) WalkerMut { switch (self.*) { .node => |*node| { const left_bols = node.weights.bols; if (line >= left_bols) { - const right_result = node.right.walk_from_line_begin_internal(a, line - left_bols, f, ctx); + const right_result = node.right.walk_from_line_begin_internal(a, line - left_bols, f, ctx, plane); if (right_result.replace) |p| { var result = WalkerMut{}; result.err = right_result.err; @@ -401,13 +399,13 @@ const Node = union(enum) { return right_result; } } - const left_result = node.left.walk_from_line_begin_internal(a, line, f, ctx); - const right_result = if (left_result.found and left_result.keep_walking) node.right.walk(a, f, ctx) else WalkerMut{}; + const left_result = node.left.walk_from_line_begin_internal(a, line, f, ctx, plane); + const right_result = if (left_result.found and left_result.keep_walking) node.right.walk(a, f, ctx, plane) else WalkerMut{}; return node.merge_results(a, left_result, right_result); }, .leaf => |*l| { if (line == 0) { - var result = f(ctx, l); + var result = f(ctx, l, plane); if (result.err) |_| { result.replace = null; return result; @@ -420,8 +418,8 @@ const Node = union(enum) { } } - pub fn walk_from_line_begin(self: *const Node, a: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque) !struct { bool, ?Root } { - const result = self.walk_from_line_begin_internal(a, line, f, ctx); + pub fn walk_from_line_begin(self: *const Node, a: Allocator, line: usize, f: WalkerMut.F, ctx: *anyopaque, plane: Plane) !struct { bool, ?Root } { + const result = self.walk_from_line_begin_internal(a, line, f, ctx, plane); if (result.err) |e| return e; return .{ result.found, result.replace }; } @@ -461,27 +459,27 @@ const Node = union(enum) { } } - const EgcF = *const fn (ctx: *anyopaque, egc: []const u8, wcwidth: usize) Walker; + const EgcF = *const fn (ctx: *anyopaque, egc: []const u8, wcwidth: usize, plane: Plane) Walker; - pub fn walk_egc_forward(self: *const Node, line: usize, walker_f: EgcF, walker_ctx: *anyopaque) !void { + pub fn walk_egc_forward(self: *const Node, line: usize, walker_f: EgcF, walker_ctx: *anyopaque, plane_: Plane) !void { const Ctx = struct { walker_f: EgcF, walker_ctx: @TypeOf(walker_ctx), abs_col: usize = 0, - fn walker(ctx_: *anyopaque, leaf: *const Self.Leaf) Walker { + fn walker(ctx_: *anyopaque, leaf: *const Self.Leaf, plane: Plane) Walker { const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); var buf: []const u8 = leaf.buf; while (buf.len > 0) { var cols: c_int = undefined; - const bytes = egc_length(buf, &cols, ctx.abs_col); - const ret = ctx.walker_f(ctx.walker_ctx, buf[0..bytes], @intCast(cols)); + const bytes = plane.egc_length(buf, &cols, ctx.abs_col); + const ret = ctx.walker_f(ctx.walker_ctx, buf[0..bytes], @intCast(cols), plane); if (ret.err) |e| return .{ .err = e }; buf = buf[bytes..]; ctx.abs_col += @intCast(cols); if (!ret.keep_walking) return Walker.stop; } if (leaf.eol) { - const ret = ctx.walker_f(ctx.walker_ctx, "\n", 1); + const ret = ctx.walker_f(ctx.walker_ctx, "\n", 1, plane); if (ret.err) |e| return .{ .err = e }; if (!ret.keep_walking) return Walker.stop; ctx.abs_col = 0; @@ -490,16 +488,16 @@ const Node = union(enum) { } }; var ctx: Ctx = .{ .walker_f = walker_f, .walker_ctx = walker_ctx }; - const found = try self.walk_from_line_begin_const(line, Ctx.walker, &ctx); + const found = try self.walk_from_line_begin_const(line, Ctx.walker, &ctx, plane_); if (!found) return error.NotFound; } - pub fn ecg_at(self: *const Node, line: usize, col: usize) error{NotFound}!struct { []const u8, usize, usize } { + pub fn ecg_at(self: *const Node, line: usize, col: usize, plane: Plane) error{NotFound}!struct { []const u8, usize, usize } { const ctx_ = struct { col: usize, at: ?[]const u8 = null, wcwidth: usize = 0, - fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize) Walker { + fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Plane) Walker { const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); ctx.at = egc; ctx.wcwidth = wcwidth; @@ -510,20 +508,20 @@ const Node = union(enum) { } }; var ctx: ctx_ = .{ .col = col }; - self.walk_egc_forward(line, ctx_.walker, &ctx) catch return .{ "?", 1, 0 }; + self.walk_egc_forward(line, ctx_.walker, &ctx, plane) catch return .{ "?", 1, 0 }; return if (ctx.at) |at| .{ at, ctx.wcwidth, ctx.col } else error.NotFound; } - pub fn test_at(self: *const Node, pred: *const fn (c: []const u8) bool, line: usize, col: usize) bool { - const ecg, _, _ = self.ecg_at(line, col) catch return false; + pub fn test_at(self: *const Node, pred: *const fn (c: []const u8) bool, line: usize, col: usize, plane: Plane) bool { + const ecg, _, _ = self.ecg_at(line, col, plane) catch return false; return pred(ecg); } - pub fn get_line_width_map(self: *const Node, line: usize, map: *ArrayList(u16)) error{ Stop, NoSpaceLeft }!void { + pub fn get_line_width_map(self: *const Node, line: usize, map: *ArrayList(u16), plane: Plane) error{ Stop, NoSpaceLeft }!void { const Ctx = struct { map: *ArrayList(u16), wcwidth: usize = 0, - fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize) Walker { + fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Plane) Walker { const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); var n = egc.len; while (n > 0) : (n -= 1) { @@ -535,20 +533,20 @@ const Node = union(enum) { } }; var ctx: Ctx = .{ .map = map }; - self.walk_egc_forward(line, Ctx.walker, &ctx) catch |e| return switch (e) { + self.walk_egc_forward(line, Ctx.walker, &ctx, plane) catch |e| return switch (e) { error.NoSpaceLeft => error.NoSpaceLeft, else => error.Stop, }; } - pub fn get_range(self: *const Node, sel: Selection, copy_buf: ?[]u8, size: ?*usize, wcwidth_: ?*usize) error{ Stop, NoSpaceLeft }!?[]u8 { + pub fn get_range(self: *const Node, sel: Selection, copy_buf: ?[]u8, size: ?*usize, wcwidth_: ?*usize, plane_: Plane) error{ Stop, NoSpaceLeft }!?[]u8 { const Ctx = struct { col: usize = 0, sel: Selection, out: ?[]u8, bytes: usize = 0, wcwidth: usize = 0, - fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize) Walker { + fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Plane) Walker { const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); if (ctx.col < ctx.sel.begin.col) { ctx.col += wcwidth; @@ -581,7 +579,7 @@ const Node = union(enum) { ctx.sel.normalize(); if (sel.begin.eql(sel.end)) return error.Stop; - self.walk_egc_forward(sel.begin.row, Ctx.walker, &ctx) catch |e| return switch (e) { + self.walk_egc_forward(sel.begin.row, Ctx.walker, &ctx, plane_) catch |e| return switch (e) { error.NoSpaceLeft => error.NoSpaceLeft, else => error.Stop, }; @@ -590,20 +588,20 @@ const Node = union(enum) { return if (copy_buf) |buf_| buf_[0..ctx.bytes] else null; } - pub fn delete_range(self: *const Node, sel: Selection, a: Allocator, size: ?*usize) error{Stop}!Root { + pub fn delete_range(self: *const Node, sel: Selection, a: Allocator, size: ?*usize, plane: Plane) error{Stop}!Root { var wcwidth: usize = 0; - _ = self.get_range(sel, null, size, &wcwidth) catch return error.Stop; - return self.del_chars(sel.begin.row, sel.begin.col, wcwidth, a) catch return error.Stop; + _ = self.get_range(sel, null, size, &wcwidth, plane) catch return error.Stop; + return self.del_chars(sel.begin.row, sel.begin.col, wcwidth, a, plane) catch return error.Stop; } - pub fn del_chars(self: *const Node, line: usize, col: usize, count: usize, a: Allocator) !Root { + pub fn del_chars(self: *const Node, line: usize, col: usize, count: usize, a: Allocator, plane_: Plane) !Root { const Ctx = struct { a: Allocator, col: usize, abs_col: usize = 0, count: usize, delete_next_bol: bool = false, - fn walker(Ctx: *anyopaque, leaf: *const Leaf) WalkerMut { + fn walker(Ctx: *anyopaque, leaf: *const Leaf, plane: Plane) WalkerMut { const ctx = @as(*@This(), @ptrCast(@alignCast(Ctx))); var result = WalkerMut.keep_walking; if (ctx.delete_next_bol and ctx.count == 0) { @@ -612,7 +610,7 @@ const Node = union(enum) { ctx.delete_next_bol = false; return result; } - const leaf_wcwidth = leaf.width(ctx.abs_col); + const leaf_wcwidth = leaf.width(ctx.abs_col, plane); const leaf_bol = leaf.bol and !ctx.delete_next_bol; ctx.delete_next_bol = false; const base_col = ctx.abs_col; @@ -636,7 +634,7 @@ const Node = union(enum) { result.replace = Leaf.new(ctx.a, "", leaf_bol, leaf.eol) catch |e| return .{ .err = e }; ctx.count = 0; } else { - const pos = leaf.width_to_pos(ctx.count, base_col) catch |e| return .{ .err = e }; + const pos = leaf.width_to_pos(ctx.count, base_col, plane) catch |e| return .{ .err = e }; result.replace = Leaf.new(ctx.a, leaf.buf[pos..], leaf_bol, leaf.eol) catch |e| return .{ .err = e }; ctx.count = 0; } @@ -650,7 +648,7 @@ const Node = union(enum) { } else { if (ctx.col + ctx.count >= leaf_wcwidth) { ctx.count -= leaf_wcwidth - ctx.col; - const pos = leaf.width_to_pos(ctx.col, base_col) catch |e| return .{ .err = e }; + const pos = leaf.width_to_pos(ctx.col, base_col, plane) catch |e| return .{ .err = e }; const leaf_eol = if (leaf.eol and ctx.count > 0) leaf_eol: { ctx.count -= 1; ctx.delete_next_bol = true; @@ -659,8 +657,8 @@ const Node = union(enum) { result.replace = Leaf.new(ctx.a, leaf.buf[0..pos], leaf_bol, leaf_eol) catch |e| return .{ .err = e }; ctx.col = 0; } else { - const pos = leaf.width_to_pos(ctx.col, base_col) catch |e| return .{ .err = e }; - const pos_end = leaf.width_to_pos(ctx.col + ctx.count, base_col) catch |e| return .{ .err = e }; + const pos = leaf.width_to_pos(ctx.col, base_col, plane) catch |e| return .{ .err = e }; + const pos_end = leaf.width_to_pos(ctx.col + ctx.count, base_col, plane) catch |e| return .{ .err = e }; const left = Leaf.new(ctx.a, leaf.buf[0..pos], leaf_bol, false) catch |e| return .{ .err = e }; const right = Leaf.new(ctx.a, leaf.buf[pos_end..], false, leaf.eol) catch |e| return .{ .err = e }; result.replace = Node.new(ctx.a, left, right) catch |e| return .{ .err = e }; @@ -674,7 +672,7 @@ const Node = union(enum) { } }; var ctx: Ctx = .{ .a = a, .col = col, .count = count }; - const found, const root = try self.walk_from_line_begin(a, line, Ctx.walker, &ctx); + const found, const root = try self.walk_from_line_begin(a, line, Ctx.walker, &ctx, plane_); return if (found) (if (root) |r| r else error.Stop) else error.NotFound; } @@ -704,36 +702,43 @@ const Node = union(enum) { return if (!found) error.NotFound; } - pub fn line_width(self: *const Node, line: usize) !usize { + pub fn line_width(self: *const Node, line: usize, plane_: Plane) !usize { const do = struct { result: usize = 0, - fn walker(ctx: *anyopaque, leaf: *const Leaf) Walker { + fn walker(ctx: *anyopaque, leaf: *const Leaf, plane: Plane) Walker { const do = @as(*@This(), @ptrCast(@alignCast(ctx))); - do.result += leaf.width(do.result); + do.result += leaf.width(do.result, plane); return if (!leaf.eol) Walker.keep_walking else Walker.stop; } }; var ctx: do = .{}; - const found = self.walk_from_line_begin_const(line, do.walker, &ctx) catch true; + const found = self.walk_from_line_begin_const(line, do.walker, &ctx, plane_) catch true; return if (found) ctx.result else error.NotFound; } - pub fn pos_to_width(self: *const Node, line: usize, pos: usize) !usize { + pub fn pos_to_width(self: *const Node, line: usize, pos: usize, plane_: Plane) !usize { const do = struct { result: usize = 0, pos: usize, - fn walker(ctx: *anyopaque, leaf: *const Leaf) Walker { + fn walker(ctx: *anyopaque, leaf: *const Leaf, plane: Plane) Walker { const do = @as(*@This(), @ptrCast(@alignCast(ctx))); - do.result += leaf.pos_to_width(&do.pos, do.result); + do.result += leaf.pos_to_width(&do.pos, do.result, plane); return if (!(leaf.eol or do.pos == 0)) Walker.keep_walking else Walker.stop; } }; var ctx: do = .{ .pos = pos }; - const found = self.walk_from_line_begin_const(line, do.walker, &ctx) catch true; + const found = self.walk_from_line_begin_const(line, do.walker, &ctx, plane_) catch true; return if (found) ctx.result else error.NotFound; } - pub fn insert_chars(self_: *const Node, line_: usize, col_: usize, s: []const u8, a: Allocator) !struct { usize, usize, Root } { + pub fn insert_chars( + self_: *const Node, + line_: usize, + col_: usize, + s: []const u8, + a: Allocator, + plane_: Plane, + ) !struct { usize, usize, Root } { var self = self_; const Ctx = struct { a: Allocator, @@ -742,9 +747,9 @@ const Node = union(enum) { s: []const u8, eol: bool, - fn walker(ctx: *anyopaque, leaf: *const Leaf) WalkerMut { + fn walker(ctx: *anyopaque, leaf: *const Leaf, plane: Plane) WalkerMut { const Ctx = @as(*@This(), @ptrCast(@alignCast(ctx))); - const leaf_wcwidth = leaf.width(Ctx.abs_col); + const leaf_wcwidth = leaf.width(Ctx.abs_col, plane); const base_col = Ctx.abs_col; Ctx.abs_col += leaf_wcwidth; @@ -776,7 +781,7 @@ const Node = union(enum) { } if (leaf_wcwidth > Ctx.col) { - const pos = leaf.width_to_pos(Ctx.col, base_col) catch |e| return .{ .err = e }; + const pos = leaf.width_to_pos(Ctx.col, base_col, plane) catch |e| return .{ .err = e }; if (Ctx.eol and Ctx.s.len == 0) { const left = Leaf.new(Ctx.a, leaf.buf[0..pos], leaf.bol, Ctx.eol) catch |e| return .{ .err = e }; const right = Leaf.new(Ctx.a, leaf.buf[pos..], Ctx.eol, leaf.eol) catch |e| return .{ .err = e }; @@ -813,14 +818,14 @@ const Node = union(enum) { need_eol = false; } var ctx: Ctx = .{ .a = a, .col = col, .s = chunk, .eol = need_eol }; - const found, const replace = try self.walk_from_line_begin(a, line, Ctx.walker, &ctx); + const found, const replace = try self.walk_from_line_begin(a, line, Ctx.walker, &ctx, plane_); if (!found) return error.NotFound; if (replace) |root| self = root; if (need_eol) { line += 1; col = 0; } else { - col += egc_chunk_width(chunk, col); + col += plane_.egc_chunk_width(chunk, col); } } return .{ line, col, self }; @@ -913,19 +918,19 @@ const Node = union(enum) { return self.store(ctx.writer()); } - pub fn debug_render_chunks(self: *const Node, line: usize, output: *ArrayList(u8)) !void { + pub fn debug_render_chunks(self: *const Node, line: usize, output: *ArrayList(u8), plane_: Plane) !void { const ctx_ = struct { l: *ArrayList(u8), wcwidth: usize = 0, - fn walker(ctx_: *anyopaque, leaf: *const Leaf) Walker { + fn walker(ctx_: *anyopaque, leaf: *const Leaf, plane: Plane) Walker { const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); - leaf.dump(ctx.l, ctx.wcwidth) catch |e| return .{ .err = e }; - ctx.wcwidth += leaf.width(ctx.wcwidth); + leaf.dump(ctx.l, ctx.wcwidth, plane) catch |e| return .{ .err = e }; + ctx.wcwidth += leaf.width(ctx.wcwidth, plane); return if (!leaf.eol) Walker.keep_walking else Walker.stop; } }; var ctx: ctx_ = .{ .l = output }; - const found = self.walk_from_line_begin_const(line, ctx_.walker, &ctx) catch true; + const found = self.walk_from_line_begin_const(line, ctx_.walker, &ctx, plane_) catch true; if (!found) return error.NotFound; var buf: [16]u8 = undefined; diff --git a/src/buffer/Cursor.zig b/src/buffer/Cursor.zig index 0906240..6dcdd26 100644 --- a/src/buffer/Cursor.zig +++ b/src/buffer/Cursor.zig @@ -1,5 +1,6 @@ const std = @import("std"); const cbor = @import("cbor"); +const Plane = @import("renderer").Plane; const Buffer = @import("Buffer.zig"); const View = @import("View.zig"); const Selection = @import("Selection.zig"); @@ -26,20 +27,20 @@ pub inline fn right_of(self: Self, other: Self) bool { return if (self.row > other.row) true else if (self.row == other.row and self.col > other.col) true else false; } -pub fn clamp_to_buffer(self: *Self, root: Buffer.Root) void { +pub fn clamp_to_buffer(self: *Self, root: Buffer.Root, plane: Plane) void { self.row = @min(self.row, root.lines() - 1); - self.col = @min(self.col, root.line_width(self.row) catch 0); + self.col = @min(self.col, root.line_width(self.row, plane) catch 0); } -fn follow_target(self: *Self, root: Buffer.Root) void { - self.col = @min(self.target, root.line_width(self.row) catch 0); +fn follow_target(self: *Self, root: Buffer.Root, plane: Plane) void { + self.col = @min(self.target, root.line_width(self.row, plane) catch 0); } -fn move_right_no_target(self: *Self, root: Buffer.Root) !void { +fn move_right_no_target(self: *Self, root: Buffer.Root, plane: Plane) !void { const lines = root.lines(); if (lines <= self.row) return error.Stop; - if (self.col < root.line_width(self.row) catch 0) { - _, const wcwidth, const offset = root.ecg_at(self.row, self.col) catch return error.Stop; + if (self.col < root.line_width(self.row, plane) catch 0) { + _, const wcwidth, const offset = root.ecg_at(self.row, self.col, plane) catch return error.Stop; self.col += wcwidth - offset; } else if (self.row < lines - 1) { self.col = 0; @@ -47,75 +48,75 @@ fn move_right_no_target(self: *Self, root: Buffer.Root) !void { } else return error.Stop; } -pub fn move_right(self: *Self, root: Buffer.Root) !void { - try self.move_right_no_target(root); +pub fn move_right(self: *Self, root: Buffer.Root, plane: Plane) !void { + try self.move_right_no_target(root, plane); self.target = self.col; } -fn move_left_no_target(self: *Self, root: Buffer.Root) !void { +fn move_left_no_target(self: *Self, root: Buffer.Root, plane: Plane) !void { if (self.col == 0) { if (self.row == 0) return error.Stop; self.row -= 1; - self.col = root.line_width(self.row) catch 0; + self.col = root.line_width(self.row, plane) catch 0; } else { - _, const wcwidth, _ = root.ecg_at(self.row, self.col - 1) catch return error.Stop; + _, const wcwidth, _ = root.ecg_at(self.row, self.col - 1, plane) catch return error.Stop; if (self.col > wcwidth) self.col -= wcwidth else self.col = 0; } } -pub fn move_left(self: *Self, root: Buffer.Root) !void { - try self.move_left_no_target(root); +pub fn move_left(self: *Self, root: Buffer.Root, plane: Plane) !void { + try self.move_left_no_target(root, plane); self.target = self.col; } -pub fn move_up(self: *Self, root: Buffer.Root) !void { +pub fn move_up(self: *Self, root: Buffer.Root, plane: Plane) !void { if (self.row > 0) { self.row -= 1; - self.follow_target(root); - self.move_left_no_target(root) catch return; - try self.move_right_no_target(root); + self.follow_target(root, plane); + self.move_left_no_target(root, plane) catch return; + try self.move_right_no_target(root, plane); } else return error.Stop; } -pub fn move_down(self: *Self, root: Buffer.Root) !void { +pub fn move_down(self: *Self, root: Buffer.Root, plane: Plane) !void { if (self.row < root.lines() - 1) { self.row += 1; - self.follow_target(root); - self.move_left_no_target(root) catch return; - try self.move_right_no_target(root); + self.follow_target(root, plane); + self.move_left_no_target(root, plane) catch return; + try self.move_right_no_target(root, plane); } else return error.Stop; } -pub fn move_page_up(self: *Self, root: Buffer.Root, view: *const View) void { +pub fn move_page_up(self: *Self, root: Buffer.Root, view: *const View, plane: Plane) void { self.row = if (self.row > view.rows) self.row - view.rows else 0; - self.follow_target(root); - self.move_left_no_target(root) catch return; - self.move_right_no_target(root) catch return; + self.follow_target(root, plane); + self.move_left_no_target(root, plane) catch return; + self.move_right_no_target(root, plane) catch return; } -pub fn move_page_down(self: *Self, root: Buffer.Root, view: *const View) void { +pub fn move_page_down(self: *Self, root: Buffer.Root, view: *const View, plane: Plane) void { if (root.lines() < view.rows) { - self.move_buffer_last(root); + self.move_buffer_last(root, plane); } else if (self.row < root.lines() - view.rows - 1) { self.row += view.rows; } else self.row = root.lines() - 1; - self.follow_target(root); - self.move_left_no_target(root) catch return; - self.move_right_no_target(root) catch return; + self.follow_target(root, plane); + self.move_left_no_target(root, plane) catch return; + self.move_right_no_target(root, plane) catch return; } -pub fn move_to(self: *Self, root: Buffer.Root, row: usize, col: usize) !void { +pub fn move_to(self: *Self, root: Buffer.Root, row: usize, col: usize, plane: Plane) !void { if (row < root.lines()) { self.row = row; - self.col = @min(col, root.line_width(self.row) catch return error.Stop); + self.col = @min(col, root.line_width(self.row, plane) catch return error.Stop); self.target = self.col; } else return error.Stop; } -pub fn move_abs(self: *Self, root: Buffer.Root, v: *View, y: usize, x: usize) !void { +pub fn move_abs(self: *Self, root: Buffer.Root, v: *View, y: usize, x: usize, plane: Plane) !void { self.row = v.row + y; self.col = v.col + x; - self.clamp_to_buffer(root); + self.clamp_to_buffer(root, plane); self.target = self.col; } @@ -124,8 +125,8 @@ pub fn move_begin(self: *Self) void { self.target = self.col; } -pub fn move_end(self: *Self, root: Buffer.Root) void { - if (self.row < root.lines()) self.col = root.line_width(self.row) catch 0; +pub fn move_end(self: *Self, root: Buffer.Root, plane: Plane) void { + if (self.row < root.lines()) self.col = root.line_width(self.row, plane) catch 0; self.target = std.math.maxInt(u32); } @@ -135,32 +136,32 @@ pub fn move_buffer_begin(self: *Self) void { self.target = 0; } -pub fn move_buffer_end(self: *Self, root: Buffer.Root) void { +pub fn move_buffer_end(self: *Self, root: Buffer.Root, plane: Plane) void { self.row = root.lines() - 1; - self.move_end(root); + self.move_end(root, plane); if (self.col == 0) self.target = 0; } -fn move_buffer_first(self: *Self, root: Buffer.Root) void { +fn move_buffer_first(self: *Self, root: Buffer.Root, plane: Plane) void { self.row = 0; - self.follow_target(root); + self.follow_target(root, plane); } -fn move_buffer_last(self: *Self, root: Buffer.Root) void { +fn move_buffer_last(self: *Self, root: Buffer.Root, plane: Plane) void { self.row = root.lines() - 1; - self.follow_target(root); + self.follow_target(root, plane); } fn is_at_begin(self: *const Self) bool { return self.col == 0; } -fn is_at_end(self: *const Self, root: Buffer.Root) bool { - return if (self.row < root.lines()) self.col == root.line_width(self.row) catch 0 else true; +fn is_at_end(self: *const Self, root: Buffer.Root, plane: Plane) bool { + return if (self.row < root.lines()) self.col == root.line_width(self.row, plane) catch 0 else true; } -pub fn test_at(self: *const Self, root: Buffer.Root, pred: *const fn (c: []const u8) bool) bool { - return root.test_at(pred, self.row, self.col); +pub fn test_at(self: *const Self, root: Buffer.Root, pred: *const fn (c: []const u8) bool, plane: Plane) bool { + return root.test_at(pred, self.row, self.col, plane); } pub fn write(self: *const Self, writer: Buffer.MetaWriter) !void { diff --git a/src/buffer/Selection.zig b/src/buffer/Selection.zig index dfd3cd4..26e142f 100644 --- a/src/buffer/Selection.zig +++ b/src/buffer/Selection.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Plane = @import("renderer").Plane; const Buffer = @import("Buffer.zig"); const Cursor = @import("Cursor.zig"); @@ -15,12 +16,12 @@ pub fn from_cursor(cursor: *const Cursor) Self { return .{ .begin = cursor.*, .end = cursor.* }; } -pub fn line_from_cursor(cursor: Cursor, root: Buffer.Root) Self { +pub fn line_from_cursor(cursor: Cursor, root: Buffer.Root, plane: Plane) Self { var begin = cursor; var end = cursor; begin.move_begin(); - end.move_end(root); - end.move_right(root) catch {}; + end.move_end(root, plane); + end.move_right(root, plane) catch {}; return .{ .begin = begin, .end = end }; } diff --git a/src/renderer/notcurses/Plane.zig b/src/renderer/notcurses/Plane.zig index aaa6367..c6aca43 100644 --- a/src/renderer/notcurses/Plane.zig +++ b/src/renderer/notcurses/Plane.zig @@ -77,15 +77,19 @@ pub const Plane = struct { return self.plane.dim_x(); } - pub fn abs_yx_to_rel(self: Plane, y: ?*c_int, x: ?*c_int) void { - self.plane.abs_yx_to_rel(y, x); + pub fn abs_yx_to_rel(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } { + var y_, var x_ = .{ y, x }; + self.plane.abs_yx_to_rel(&y_, &x_); + return .{ y_, x_ }; } - pub fn rel_yx_to_abs(self: Plane, y: ?*c_int, x: ?*c_int) void { - self.plane.rel_yx_to_abs(y, x); + pub fn rel_yx_to_abs(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } { + var y_, var x_ = .{ y, x }; + self.plane.rel_yx_to_abs(&y_, &x_); + return .{ y_, x_ }; } - pub fn move_bottom(self: Plane) void { + pub fn hide(self: Plane) void { self.plane.move_bottom(); } @@ -244,4 +248,30 @@ pub const Plane = struct { .strikethrough => plane.plane.set_styles(nc.style.struck), }; } + + pub fn egc_length(_: Plane, egcs: []const u8, colcount: *c_int, abs_col: usize) usize { + if (egcs[0] == '\t') { + colcount.* = @intCast(8 - abs_col % 8); + return 1; + } + return nc.ncegc_len(egcs, colcount) catch ret: { + colcount.* = 1; + break :ret 1; + }; + } + + pub fn egc_chunk_width(plane: Plane, chunk_: []const u8, abs_col_: usize) usize { + var abs_col = abs_col_; + var chunk = chunk_; + var colcount: usize = 0; + var cols: c_int = 0; + while (chunk.len > 0) { + const bytes = plane.egc_length(chunk, &cols, abs_col); + colcount += @intCast(cols); + abs_col += @intCast(cols); + if (chunk.len < bytes) break; + chunk = chunk[bytes..]; + } + return colcount; + } }; diff --git a/src/renderer/notcurses/egc.zig b/src/renderer/notcurses/egc.zig deleted file mode 100644 index 746ea84..0000000 --- a/src/renderer/notcurses/egc.zig +++ /dev/null @@ -1,29 +0,0 @@ -const nc = @import("notcurses"); - -pub fn length(egcs: []const u8, colcount: *c_int, abs_col: usize) usize { - if (egcs[0] == '\t') { - colcount.* = @intCast(8 - abs_col % 8); - return 1; - } - return nc.ncegc_len(egcs, colcount) catch ret: { - colcount.* = 1; - break :ret 1; - }; -} - -pub fn chunk_width(chunk_: []const u8, abs_col_: usize) usize { - var abs_col = abs_col_; - var chunk = chunk_; - var colcount: usize = 0; - var cols: c_int = 0; - while (chunk.len > 0) { - const bytes = length(chunk, &cols, abs_col); - colcount += @intCast(cols); - abs_col += @intCast(cols); - if (chunk.len < bytes) break; - chunk = chunk[bytes..]; - } - return colcount; -} - -pub const ucs32_to_utf8 = nc.ucs32_to_utf8; diff --git a/src/renderer/notcurses/input.zig b/src/renderer/notcurses/input.zig index 4c4e7e2..b50c8fa 100644 --- a/src/renderer/notcurses/input.zig +++ b/src/renderer/notcurses/input.zig @@ -1,6 +1,7 @@ const nc = @import("notcurses"); pub const key = nc.key; +pub const key_type = u32; pub const modifier = nc.mod; pub const event_type = nc.event_type; diff --git a/src/renderer/notcurses/renderer.zig b/src/renderer/notcurses/renderer.zig index 9c2bc64..b8cbb1c 100644 --- a/src/renderer/notcurses/renderer.zig +++ b/src/renderer/notcurses/renderer.zig @@ -4,7 +4,6 @@ const log = @import("log"); const nc = @import("notcurses"); const Style = @import("theme").Style; -pub const egc = @import("egc.zig"); pub const input = @import("input.zig"); pub const Plane = @import("Plane.zig").Plane; @@ -18,7 +17,7 @@ const key = input.key; const event_type = input.event_type; const Self = @This(); -const log_name = "ncrender"; +pub const log_name = "notcurses"; a: std.mem.Allocator, ctx: nc.Context, @@ -79,6 +78,8 @@ pub fn deinit(self: *Self) void { self.bracketed_paste_buffer.deinit(); } +pub fn run(_: *Self) !void {} + pub fn render(self: Self) !void { return self.ctx.render(); } @@ -103,11 +104,25 @@ pub fn leave_alternate_screen(self: Self) void { return self.ctx.leave_alternate_screen(); } -pub fn process_input(self: *Self) !void { +const InputError = error{ + OutOfMemory, + InvalidCharacter, + NoSpaceLeft, + CborIntegerTooLarge, + CborIntegerTooSmall, + CborInvalidType, + CborTooShort, + Ucs32toUtf8Error, + InvalidPadding, + ReadInputError, + WouldBlock, +}; + +pub fn process_input(self: *Self) InputError!void { var input_buffer: [256]nc.Input = undefined; while (true) { - const nivec = try self.ctx.getvec_nblock(&input_buffer); + const nivec = self.ctx.getvec_nblock(&input_buffer) catch return error.ReadInputError; if (nivec.len == 0) break; for (nivec) |*ni| { @@ -191,7 +206,7 @@ fn handle_bracketed_paste_input(self: *Self, cbor_msg: []const u8) !bool { key.ENTER => try self.bracketed_paste_buffer.appendSlice("\n"), else => if (!key.synthesized_p(keypress)) { var buf: [6]u8 = undefined; - const bytes = try egc.ucs32_to_utf8(&[_]u32{egc_}, &buf); + const bytes = try ucs32_to_utf8(&[_]u32{egc_}, &buf); try self.bracketed_paste_buffer.appendSlice(buf[0..bytes]); } else { try self.handle_bracketed_paste_end(); @@ -506,3 +521,5 @@ pub fn cursor_enable(self: Self, y: c_int, x: c_int) !void { pub fn cursor_disable(self: Self) void { self.ctx.cursor_disable() catch {}; } + +pub const ucs32_to_utf8 = nc.ucs32_to_utf8; diff --git a/src/renderer/vaxis/Cell.zig b/src/renderer/vaxis/Cell.zig new file mode 100644 index 0000000..5178f23 --- /dev/null +++ b/src/renderer/vaxis/Cell.zig @@ -0,0 +1,51 @@ +const vaxis = @import("vaxis"); +const Style = @import("theme").Style; + +const Cell = @This(); + +cell: vaxis.Cell = .{}, + +pub inline fn set_style(self: *Cell, style_: Style) void { + if (style_.fg) |fg| self.cell.style.fg = vaxis.Cell.Color.rgbFromUint(fg); + if (style_.bg) |bg| self.cell.style.bg = vaxis.Cell.Color.rgbFromUint(bg); + if (style_.fs) |fs| { + self.cell.style.ul = .default; + self.cell.style.ul_style = .off; + self.cell.style.bold = false; + self.cell.style.dim = false; + self.cell.style.italic = false; + self.cell.style.blink = false; + self.cell.style.reverse = false; + self.cell.style.invisible = false; + self.cell.style.strikethrough = false; + + switch (fs) { + .normal => {}, + .bold => self.cell.style.bold = true, + .italic => self.cell.style.italic = false, + .underline => self.cell.style.ul_style = .single, + .undercurl => self.cell.style.ul_style = .curly, + .strikethrough => self.cell.style.strikethrough = true, + } + } +} + +pub inline fn set_style_fg(self: *Cell, style_: Style) void { + if (style_.fg) |fg| self.cell.style.fg = vaxis.Cell.Color.rgbFromUint(fg); +} + +pub inline fn set_style_bg(self: *Cell, style_: Style) void { + if (style_.bg) |bg| self.cell.style.bg = vaxis.Cell.Color.rgbFromUint(bg); +} + +pub inline fn set_fg_rgb(self: *Cell, arg_rgb: c_uint) !void { + self.cell.style.fg = vaxis.Cell.Color.rgbFromUint(@intCast(arg_rgb)); +} +pub inline fn set_bg_rgb(self: *Cell, arg_rgb: c_uint) !void { + self.cell.style.bg = vaxis.Cell.Color.rgbFromUint(@intCast(arg_rgb)); +} + +pub fn columns(self: *const Cell) usize { + // return if (self.cell.char.width == 0) self.window.gwidth(self.cell.char.grapheme) else self.cell.char.width; // FIXME? + return self.cell.char.width; +} diff --git a/src/renderer/vaxis/Plane.zig b/src/renderer/vaxis/Plane.zig new file mode 100644 index 0000000..5a1540d --- /dev/null +++ b/src/renderer/vaxis/Plane.zig @@ -0,0 +1,346 @@ +const std = @import("std"); +const Style = @import("theme").Style; +const StyleBits = @import("style.zig").StyleBits; +const Cell = @import("Cell.zig"); +const vaxis = @import("vaxis"); + +const Plane = @This(); + +window: vaxis.Window, +row: i32 = 0, +col: i32 = 0, +name_buf: [128]u8, +name_len: usize, +cache: GraphemeCache = .{}, +style: vaxis.Cell.Style = .{}, + +pub const Options = struct { + y: usize = 0, + x: usize = 0, + rows: usize = 0, + cols: usize = 0, + name: [*:0]const u8, + flags: option = .none, +}; + +pub const option = enum { + none, + VSCROLL, +}; + +pub fn init(nopts: *const Options, parent_: Plane) !Plane { + const opts = .{ + .x_off = nopts.x, + .y_off = nopts.y, + .width = .{ .limit = nopts.cols }, + .height = .{ .limit = nopts.rows }, + .border = .{}, + }; + var plane: Plane = .{ + .window = parent_.window.child(opts), + .name_buf = undefined, + .name_len = std.mem.span(nopts.name).len, + }; + @memcpy(plane.name_buf[0..plane.name_len], nopts.name); + return plane; +} + +pub fn deinit(_: *Plane) void {} + +pub fn name(self: Plane, buf: []u8) []const u8 { + @memcpy(buf[0..self.name_len], self.name_buf[0..self.name_len]); + return buf[0..self.name_len]; +} + +pub fn above(_: Plane) ?Plane { + return null; +} + +pub fn below(_: Plane) ?Plane { + return null; +} + +pub fn erase(self: Plane) void { + self.window.clear(); +} + +pub inline fn abs_y(self: Plane) c_int { + return @intCast(self.window.y_off); +} + +pub inline fn abs_x(self: Plane) c_int { + return @intCast(self.window.x_off); +} + +pub inline fn dim_y(self: Plane) c_uint { + return @intCast(self.window.height); +} + +pub inline fn dim_x(self: Plane) c_uint { + return @intCast(self.window.width); +} + +pub fn abs_yx_to_rel(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } { + return .{ y - self.abs_y(), x - self.abs_x() }; +} + +pub fn rel_yx_to_abs(self: Plane, y: c_int, x: c_int) struct { c_int, c_int } { + return .{ self.abs_y() + y, self.abs_x() + x }; +} + +pub fn hide(_: Plane) void {} + +pub fn move_yx(self: *Plane, y: c_int, x: c_int) !void { + self.window.y_off = @intCast(y); + self.window.x_off = @intCast(x); +} + +pub fn resize_simple(self: *Plane, ylen: c_uint, xlen: c_uint) !void { + self.window.height = @intCast(ylen); + self.window.width = @intCast(xlen); +} + +pub fn home(self: *Plane) void { + self.row = 0; + self.col = 0; +} + +pub fn print(self: *Plane, comptime fmt: anytype, args: anytype) !usize { + var buf: [fmt.len + 4096]u8 = undefined; + const text = try std.fmt.bufPrint(&buf, fmt, args); + return self.putstr(text); +} + +pub fn print_aligned_right(self: *Plane, y: c_int, comptime fmt: anytype, args: anytype) !usize { + var buf: [fmt.len + 4096]u8 = undefined; + const width = self.window.width; + const text = try std.fmt.bufPrint(&buf, fmt, args); + const text_width = self.egc_chunk_width(text, 0); + self.row = @intCast(y); + self.col = @intCast(if (text_width >= width) 0 else width - text_width); + return self.putstr(text); +} + +pub fn print_aligned_center(self: *Plane, y: c_int, comptime fmt: anytype, args: anytype) !usize { + var buf: [fmt.len + 4096]u8 = undefined; + const width = self.window.width; + const text = try std.fmt.bufPrint(&buf, fmt, args); + const text_width = self.egc_chunk_width(text, 0); + self.row = @intCast(y); + self.col = @intCast(if (text_width >= width) 0 else (width - text_width) / 2); + return self.putstr(text); +} + +pub fn putstr(self: *Plane, text: []const u8) !usize { + var result: usize = 0; + const width = self.window.width; + var iter = self.window.screen.unicode.graphemeIterator(text); + while (iter.next()) |grapheme| { + if (self.col >= width) { + self.row += 1; + self.col = 0; + } + const s = grapheme.bytes(text); + if (std.mem.eql(u8, s, "\n")) { + self.row += 1; + self.col = 0; + result += 1; + continue; + } + const w = self.window.gwidth(s); + if (w == 0) continue; + self.window.writeCell(@intCast(self.col), @intCast(self.row), .{ + .char = .{ + .grapheme = self.cache.put(s), + .width = w, + }, + .style = self.style, + }); + self.col += @intCast(w); + result += 1; + } + return result; +} + +pub fn putc(self: *Plane, cell: *const Cell) !usize { + return self.putc_yx(@intCast(self.row), @intCast(self.col), cell); +} + +pub fn putc_yx(self: *Plane, y: c_int, x: c_int, cell: *const Cell) !usize { + try self.cursor_move_yx(y, x); + const w = if (cell.cell.char.width == 0) self.window.gwidth(cell.cell.char.grapheme) else cell.cell.char.width; + if (w == 0) return w; + self.window.writeCell(@intCast(self.col), @intCast(self.row), cell.cell); + self.col += @intCast(w); + if (self.col >= self.window.width) { + self.row += 1; + self.col = 0; + } + return w; +} + +pub fn cursor_yx(self: Plane, y: *c_uint, x: *c_uint) void { + y.* = @intCast(self.row); + x.* = @intCast(self.col); +} + +pub fn cursor_y(self: Plane) c_uint { + return @intCast(self.row); +} + +pub fn cursor_x(self: Plane) c_uint { + return @intCast(self.col); +} + +pub fn cursor_move_yx(self: *Plane, y: c_int, x: c_int) !void { + if (self.window.height == 0 or self.window.width == 0) return; + if (self.window.height <= y or self.window.width <= x) return; + if (y >= 0) + self.row = @intCast(y); + if (x >= 0) + self.col = @intCast(x); +} + +pub fn cursor_move_rel(self: *Plane, y: c_int, x: c_int) !void { + if (self.window.height == 0 or self.window.width == 0) return error.OutOfBounds; + const new_y: isize = @as(c_int, @intCast(self.row)) + y; + const new_x: isize = @as(c_int, @intCast(self.col)) + x; + if (new_y < 0 or new_x < 0) return error.OutOfBounds; + if (self.window.height <= new_y or self.window.width <= new_x) return error.OutOfBounds; + self.row = @intCast(new_y); + self.col = @intCast(new_x); +} + +pub fn cell_init(self: Plane) Cell { + return .{ .cell = .{ .style = self.style } }; +} + +pub fn cell_load(self: *Plane, cell: *Cell, gcluster: [:0]const u8) !usize { + cell.* = .{ .cell = .{ .style = self.style } }; + var cols: c_int = 0; + const bytes = self.egc_length(gcluster, &cols, 0); + cell.cell.char.grapheme = self.cache.put(gcluster[0..bytes]); + cell.cell.char.width = @intCast(cols); + return bytes; +} + +pub fn at_cursor_cell(self: Plane, cell: *Cell) !usize { + cell.* = .{}; + if (self.window.readCell(@intCast(self.col), @intCast(self.row))) |cell_| cell.cell = cell_; + return cell.cell.char.grapheme.len; +} + +pub fn set_styles(self: Plane, stylebits: StyleBits) void { + _ = self; + _ = stylebits; + // FIXME +} + +pub fn on_styles(self: Plane, stylebits: StyleBits) void { + _ = self; + _ = stylebits; + // FIXME +} + +pub fn off_styles(self: Plane, stylebits: StyleBits) void { + _ = self; + _ = stylebits; + // FIXME +} + +pub fn set_fg_rgb(self: *Plane, channel: u32) !void { + self.style.fg = vaxis.Cell.Color.rgbFromUint(@intCast(channel)); +} + +pub fn set_bg_rgb(self: *Plane, channel: u32) !void { + self.style.bg = vaxis.Cell.Color.rgbFromUint(@intCast(channel)); +} + +pub fn set_fg_palindex(self: *Plane, idx: c_uint) !void { + self.style.fg = .{ .index = @intCast(idx) }; +} + +pub fn set_bg_palindex(self: *Plane, idx: c_uint) !void { + self.style.bg = .{ .index = @intCast(idx) }; +} + +pub fn set_channels(self: Plane, channels_: u64) void { + _ = self; + _ = channels_; + // FIXME +} + +pub inline fn set_base_style(self: *const Plane, egc_: [*c]const u8, style_: Style) void { + _ = self; + _ = egc_; + _ = style_; + // FIXME +} + +pub fn set_base_style_transparent(self: Plane, egc_: [*:0]const u8, style_: Style) void { + _ = self; + _ = egc_; + _ = style_; + // FIXME +} + +pub fn set_base_style_bg_transparent(self: Plane, egc_: [*:0]const u8, style_: Style) void { + _ = self; + _ = egc_; + _ = style_; + // FIXME +} + +pub inline fn set_style(self: *Plane, style_: Style) void { + if (style_.fg) |color| self.style.fg = vaxis.Cell.Color.rgbFromUint(@intCast(color)); + if (style_.bg) |color| self.style.bg = vaxis.Cell.Color.rgbFromUint(@intCast(color)); + // if (style_.fs) |fontstyle| ... FIXME +} + +pub inline fn set_style_bg_transparent(self: *Plane, style_: Style) void { + if (style_.fg) |color| self.style.fg = vaxis.Cell.Color.rgbFromUint(@intCast(color)); + self.style.bg = .default; +} + +pub fn egc_length(self: *const Plane, egcs: []const u8, colcount: *c_int, abs_col: usize) usize { + if (egcs[0] == '\t') { + colcount.* = @intCast(8 - abs_col % 8); + return 1; + } + var iter = self.window.screen.unicode.graphemeIterator(egcs); + const grapheme = iter.next() orelse { + colcount.* = 1; + return 1; + }; + const s = grapheme.bytes(egcs); + const w = self.window.gwidth(s); + colcount.* = @intCast(w); + return s.len; +} + +pub fn egc_chunk_width(self: *const Plane, chunk_: []const u8, abs_col_: usize) usize { + var abs_col = abs_col_; + var chunk = chunk_; + var colcount: usize = 0; + var cols: c_int = 0; + while (chunk.len > 0) { + const bytes = self.egc_length(chunk, &cols, abs_col); + colcount += @intCast(cols); + abs_col += @intCast(cols); + if (chunk.len < bytes) break; + chunk = chunk[bytes..]; + } + return colcount; +} + +const GraphemeCache = struct { + buf: [1024 * 16]u8 = undefined, + idx: usize = 0, + + pub fn put(self: *GraphemeCache, bytes: []const u8) []u8 { + if (self.idx + bytes.len > self.buf.len) self.idx = 0; + defer self.idx += bytes.len; + @memcpy(self.buf[self.idx .. self.idx + bytes.len], bytes); + return self.buf[self.idx .. self.idx + bytes.len]; + } +}; diff --git a/src/renderer/vaxis/input.zig b/src/renderer/vaxis/input.zig new file mode 100644 index 0000000..29850da --- /dev/null +++ b/src/renderer/vaxis/input.zig @@ -0,0 +1,267 @@ +const Key = @import("vaxis").Key; +const Mouse = @import("vaxis").Mouse; + +pub const key = struct { + pub const ENTER: key_type = Key.enter; + pub const TAB: key_type = Key.tab; + pub const ESC: key_type = Key.escape; + pub const SPACE: key_type = Key.space; + pub const BACKSPACE: key_type = Key.backspace; + + pub const INS: key_type = Key.insert; + pub const DEL: key_type = Key.delete; + pub const LEFT: key_type = Key.left; + pub const RIGHT: key_type = Key.right; + pub const UP: key_type = Key.up; + pub const DOWN: key_type = Key.down; + pub const PGDOWN: key_type = Key.page_down; + pub const PGUP: key_type = Key.page_up; + pub const HOME: key_type = Key.home; + pub const END: key_type = Key.end; + pub const CAPS_LOCK: key_type = Key.caps_lock; + pub const SCROLL_LOCK: key_type = Key.scroll_lock; + pub const NUM_LOCK: key_type = Key.num_lock; + pub const PRINT_SCREEN: key_type = Key.print_screen; + pub const PAUSE: key_type = Key.pause; + pub const MENU: key_type = Key.menu; + pub const F01: key_type = Key.f1; + pub const F02: key_type = Key.f2; + pub const F03: key_type = Key.f3; + pub const F04: key_type = Key.f4; + pub const F05: key_type = Key.f5; + pub const F06: key_type = Key.f6; + pub const F07: key_type = Key.f7; + pub const F08: key_type = Key.f8; + pub const F09: key_type = Key.f9; + pub const F10: key_type = Key.f10; + pub const F11: key_type = Key.f11; + pub const F12: key_type = Key.f12; + pub const F13: key_type = Key.f13; + pub const F14: key_type = Key.f14; + pub const F15: key_type = Key.f15; + pub const F16: key_type = Key.f16; + pub const F17: key_type = Key.f17; + pub const F18: key_type = Key.f18; + pub const F19: key_type = Key.f19; + pub const F20: key_type = Key.f20; + pub const F21: key_type = Key.f21; + pub const F22: key_type = Key.f22; + pub const F23: key_type = Key.f23; + pub const F24: key_type = Key.f24; + pub const F25: key_type = Key.f25; + pub const F26: key_type = Key.f26; + pub const F27: key_type = Key.f27; + pub const F28: key_type = Key.f28; + pub const F29: key_type = Key.f29; + pub const F30: key_type = Key.f30; + pub const F31: key_type = Key.f31; + pub const F32: key_type = Key.f32; + pub const F33: key_type = Key.f33; + pub const F34: key_type = Key.f34; + pub const F35: key_type = Key.f35; + + pub const F58: key_type = Key.iso_level_5_shift + 1; // FIXME bogus + + pub const MEDIA_PLAY: key_type = Key.media_play; + pub const MEDIA_PAUSE: key_type = Key.media_pause; + pub const MEDIA_PPAUSE: key_type = Key.media_play_pause; + pub const MEDIA_REV: key_type = Key.media_reverse; + pub const MEDIA_STOP: key_type = Key.media_stop; + pub const MEDIA_FF: key_type = Key.media_fast_forward; + pub const MEDIA_REWIND: key_type = Key.media_rewind; + pub const MEDIA_NEXT: key_type = Key.media_track_next; + pub const MEDIA_PREV: key_type = Key.media_track_previous; + pub const MEDIA_RECORD: key_type = Key.media_record; + pub const MEDIA_LVOL: key_type = Key.lower_volume; + pub const MEDIA_RVOL: key_type = Key.raise_volume; + pub const MEDIA_MUTE: key_type = Key.mute_volume; + pub const LSHIFT: key_type = Key.left_shift; + pub const LCTRL: key_type = Key.left_control; + pub const LALT: key_type = Key.left_alt; + pub const LSUPER: key_type = Key.left_super; + pub const LHYPER: key_type = Key.left_hyper; + pub const LMETA: key_type = Key.left_meta; + pub const RSHIFT: key_type = Key.right_shift; + pub const RCTRL: key_type = Key.right_control; + pub const RALT: key_type = Key.right_alt; + pub const RSUPER: key_type = Key.right_super; + pub const RHYPER: key_type = Key.right_hyper; + pub const RMETA: key_type = Key.right_meta; + pub const L3SHIFT: key_type = Key.iso_level_3_shift; + pub const L5SHIFT: key_type = Key.iso_level_5_shift; + + pub const MOTION: key_type = @intCast(@intFromEnum(Mouse.Button.none)); + pub const BUTTON1: key_type = @intCast(@intFromEnum(Mouse.Button.left)); + pub const BUTTON2: key_type = @intCast(@intFromEnum(Mouse.Button.middle)); + pub const BUTTON3: key_type = @intCast(@intFromEnum(Mouse.Button.right)); + pub const BUTTON4: key_type = @intCast(@intFromEnum(Mouse.Button.wheel_up)); + pub const BUTTON5: key_type = @intCast(@intFromEnum(Mouse.Button.wheel_down)); + // pub const BUTTON6: key_type = @intCast(@intFromEnum(Mouse.Button.button_6)); + // pub const BUTTON7: key_type = @intCast(@intFromEnum(Mouse.Button.button_7)); + pub const BUTTON8: key_type = @intCast(@intFromEnum(Mouse.Button.button_8)); + pub const BUTTON9: key_type = @intCast(@intFromEnum(Mouse.Button.button_9)); + pub const BUTTON10: key_type = @intCast(@intFromEnum(Mouse.Button.button_10)); + pub const BUTTON11: key_type = @intCast(@intFromEnum(Mouse.Button.button_11)); + + // pub const SIGNAL: key_type = Key.SIGNAL; + // pub const EOF: key_type = Key.EOF; + // pub const SCROLL_UP: key_type = Key.SCROLL_UP; + // pub const SCROLL_DOWN: key_type = Key.SCROLL_DOWN; + + /// Is this uint32_t a synthesized event? + pub fn synthesized_p(w: u32) bool { + return switch (w) { + Key.up...Key.iso_level_5_shift => true, + Key.enter => true, + Key.tab => true, + Key.escape => true, + Key.space => true, + Key.backspace => true, + else => false, + }; + } +}; +pub const key_type = u21; + +pub const modifier = struct { + pub const SHIFT: modifier_type = 1; + pub const ALT: modifier_type = 2; + pub const CTRL: modifier_type = 4; + pub const SUPER: modifier_type = 8; + pub const HYPER: modifier_type = 16; + pub const META: modifier_type = 32; + pub const CAPSLOCK: modifier_type = 64; + pub const NUMLOCK: modifier_type = 128; +}; +pub const modifier_type = u32; + +pub const event_type = struct { + pub const PRESS: usize = 1; + pub const REPEAT: usize = 2; + pub const RELEASE: usize = 3; +}; + +pub const utils = struct { + pub fn isSuper(modifiers: u32) bool { + return modifiers & modifier.SUPER != 0; + } + + pub fn isCtrl(modifiers: u32) bool { + return modifiers & modifier.CTRL != 0; + } + + pub fn isShift(modifiers: u32) bool { + return modifiers & modifier.SHIFT != 0; + } + + pub fn isAlt(modifiers: u32) bool { + return modifiers & modifier.ALT != 0; + } + + pub fn key_id_string(k: u32) []const u8 { + return switch (k) { + key.ENTER => "ENTER", + key.TAB => "TAB", + key.ESC => "ESC", + key.SPACE => "SPACE", + key.BACKSPACE => "BACKSPACE", + key.INS => "INS", + key.DEL => "DEL", + key.LEFT => "LEFT", + key.RIGHT => "RIGHT", + key.UP => "UP", + key.DOWN => "DOWN", + key.PGDOWN => "PGDOWN", + key.PGUP => "PGUP", + key.HOME => "HOME", + key.END => "END", + key.CAPS_LOCK => "CAPS_LOCK", + key.SCROLL_LOCK => "SCROLL_LOCK", + key.NUM_LOCK => "NUM_LOCK", + key.PRINT_SCREEN => "PRINT_SCREEN", + key.PAUSE => "PAUSE", + key.MENU => "MENU", + key.F01 => "F01", + key.F02 => "F02", + key.F03 => "F03", + key.F04 => "F04", + key.F05 => "F05", + key.F06 => "F06", + key.F07 => "F07", + key.F08 => "F08", + key.F09 => "F09", + key.F10 => "F10", + key.F11 => "F11", + key.F12 => "F12", + key.F13 => "F13", + key.F14 => "F14", + key.F15 => "F15", + key.F16 => "F16", + key.F17 => "F17", + key.F18 => "F18", + key.F19 => "F19", + key.F20 => "F20", + key.F21 => "F21", + key.F22 => "F22", + key.F23 => "F23", + key.F24 => "F24", + key.F25 => "F25", + key.F26 => "F26", + key.F27 => "F27", + key.F28 => "F28", + key.F29 => "F29", + key.F30 => "F30", + key.F31 => "F31", + key.F32 => "F32", + key.F33 => "F33", + key.F34 => "F34", + key.F35 => "F35", + key.MEDIA_PLAY => "MEDIA_PLAY", + key.MEDIA_PAUSE => "MEDIA_PAUSE", + key.MEDIA_PPAUSE => "MEDIA_PPAUSE", + key.MEDIA_REV => "MEDIA_REV", + key.MEDIA_STOP => "MEDIA_STOP", + key.MEDIA_FF => "MEDIA_FF", + key.MEDIA_REWIND => "MEDIA_REWIND", + key.MEDIA_NEXT => "MEDIA_NEXT", + key.MEDIA_PREV => "MEDIA_PREV", + key.MEDIA_RECORD => "MEDIA_RECORD", + key.MEDIA_LVOL => "MEDIA_LVOL", + key.MEDIA_RVOL => "MEDIA_RVOL", + key.MEDIA_MUTE => "MEDIA_MUTE", + key.LSHIFT => "LSHIFT", + key.LCTRL => "LCTRL", + key.LALT => "LALT", + key.LSUPER => "LSUPER", + key.LHYPER => "LHYPER", + key.LMETA => "LMETA", + key.RSHIFT => "RSHIFT", + key.RCTRL => "RCTRL", + key.RALT => "RALT", + key.RSUPER => "RSUPER", + key.RHYPER => "RHYPER", + key.RMETA => "RMETA", + key.L3SHIFT => "L3SHIFT", + key.L5SHIFT => "L5SHIFT", + else => "", + }; + } + + pub fn button_id_string(k: u32) []const u8 { + return switch (k) { + key.MOTION => "MOTION", + key.BUTTON1 => "BUTTON1", + key.BUTTON2 => "BUTTON2", + key.BUTTON3 => "BUTTON3", + key.BUTTON4 => "BUTTON4", + key.BUTTON5 => "BUTTON5", + // key.BUTTON6 => "BUTTON6", + // key.BUTTON7 => "BUTTON7", + key.BUTTON8 => "BUTTON8", + key.BUTTON9 => "BUTTON9", + key.BUTTON10 => "BUTTON10", + key.BUTTON11 => "BUTTON11", + else => "", + }; + } +}; diff --git a/src/renderer/vaxis/renderer.zig b/src/renderer/vaxis/renderer.zig new file mode 100644 index 0000000..ac626ba --- /dev/null +++ b/src/renderer/vaxis/renderer.zig @@ -0,0 +1,312 @@ +const std = @import("std"); +const cbor = @import("cbor"); +const log = @import("log"); +const Style = @import("theme").Style; + +const vaxis = @import("vaxis"); + +pub const input = @import("input.zig"); + +pub const Plane = @import("Plane.zig"); +pub const Cell = @import("Cell.zig"); + +pub const style = @import("style.zig").StyleBits; + +const mod = input.modifier; +const key = input.key; +const event_type = input.event_type; + +const Self = @This(); +pub const log_name = "vaxis"; + +a: std.mem.Allocator, + +vx: vaxis.Vaxis, + +no_alternate: bool, +event_buffer: std.ArrayList(u8), + +bracketed_paste: bool = false, +bracketed_paste_buffer: std.ArrayList(u8), + +handler_ctx: *anyopaque, +dispatch_input: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null, +dispatch_mouse: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, cbor_msg: []const u8) void = null, +dispatch_mouse_drag: ?*const fn (ctx: *anyopaque, y: c_int, x: c_int, dragging: bool, cbor_msg: []const u8) void = null, +dispatch_event: ?*const fn (ctx: *anyopaque, cbor_msg: []const u8) void = null, + +logger: log.Logger, + +const ModState = struct { ctrl: bool = false, shift: bool = false, alt: bool = false }; + +const Event = union(enum) { + key_press: vaxis.Key, + winsize: vaxis.Winsize, + focus_in, +}; + +pub fn init(a: std.mem.Allocator, handler_ctx: *anyopaque, no_alternate: bool) !Self { + return .{ + .a = a, + .vx = try vaxis.init(a, .{}), + .no_alternate = no_alternate, + .event_buffer = std.ArrayList(u8).init(a), + .bracketed_paste_buffer = std.ArrayList(u8).init(a), + .handler_ctx = handler_ctx, + .logger = log.logger(log_name), + }; +} + +pub fn deinit(self: *Self) void { + self.vx.screen.tty.write(vaxis.ctlseqs.show_cursor); + self.vx.screen.tty.flush(); + self.vx.deinit(self.a); + self.bracketed_paste_buffer.deinit(); + self.event_buffer.deinit(); +} + +pub fn run(self: *Self) !void { + if (self.vx.tty == null) self.vx.tty = try vaxis.Tty.init(); + if (!self.no_alternate) try self.vx.enterAltScreen(); + try self.vx.queryTerminal(); + const ws = try vaxis.Tty.getWinsize(self.input_fd()); + try self.vx.resize(self.a, ws); + self.vx.queueRefresh(); + try self.vx.setMouseMode(true); +} + +pub fn render(self: *Self) !void { + self.vx.queueRefresh(); // FIXME: why do differential updates not work? + return self.vx.render(); +} + +pub fn refresh(self: *Self) !void { + const ws = try vaxis.Tty.getWinsize(self.input_fd()); + try self.vx.resize(self.a, ws); + self.vx.queueRefresh(); +} + +pub fn stop(self: *Self) void { + _ = self; +} + +pub fn stdplane(self: *Self) Plane { + const name = "root"; + var plane: Plane = .{ + .window = self.vx.window(), + .name_buf = undefined, + .name_len = name.len, + }; + @memcpy(plane.name_buf[0..name.len], name); + return plane; +} + +pub fn input_fd(self: Self) i32 { + return self.vx.tty.?.fd; +} + +pub fn leave_alternate_screen(self: *Self) void { + self.vx.exitAltScreen() catch {}; +} + +pub fn process_input(self: *Self) !void { + var parser: vaxis.Parser = .{ + .grapheme_data = &self.vx.screen.unicode.grapheme_data, + }; + var buf: [1024]u8 = undefined; + var start: usize = 0; + const n = std.posix.read(self.input_fd(), &buf) catch |e| switch (e) { + error.WouldBlock => return, + else => return e, + }; + while (start < n) { + const result = try parser.parse(buf[start..n]); + start += result.n; + const event = result.event orelse continue; + switch (event) { + .key_press => |key_| { + const cbor_msg = try self.fmtmsg(.{ + "I", + event_type.PRESS, + key_.codepoint, + key_.shifted_codepoint orelse key_.codepoint, + key_.text orelse input.utils.key_id_string(key_.codepoint), + @as(u8, @bitCast(key_.mods)), + }); + if (self.dispatch_input) |f| f(self.handler_ctx, cbor_msg); + }, + .key_release => |*key_| { + const cbor_msg = try self.fmtmsg(.{ + "I", + event_type.RELEASE, + key_.codepoint, + key_.shifted_codepoint orelse key_.codepoint, + key_.text orelse input.utils.key_id_string(key_.codepoint), + @as(u8, @bitCast(key_.mods)), + }); + if (self.dispatch_input) |f| f(self.handler_ctx, cbor_msg); + }, + .mouse => |mouse| { + if (self.dispatch_mouse) |f| switch (mouse.type) { + .motion => f(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), try self.fmtmsg(.{ + "M", + mouse.col, + mouse.row, + 0, + 0, + })), + .press => f(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), try self.fmtmsg(.{ + "B", + event_type.PRESS, + @intFromEnum(mouse.button), + input.utils.button_id_string(@intFromEnum(mouse.button)), + mouse.col, + mouse.row, + 0, + 0, + })), + .release => f(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), try self.fmtmsg(.{ + "B", + event_type.RELEASE, + @intFromEnum(mouse.button), + input.utils.button_id_string(@intFromEnum(mouse.button)), + mouse.col, + mouse.row, + 0, + 0, + })), + .drag => f(self.handler_ctx, @intCast(mouse.row), @intCast(mouse.col), try self.fmtmsg(.{ + "D", + event_type.PRESS, + @intFromEnum(mouse.button), + input.utils.button_id_string(@intFromEnum(mouse.button)), + mouse.col, + mouse.row, + 0, + 0, + })), + }; + }, + .focus_in => { + // FIXME + }, + .focus_out => { + // FIXME + }, + .paste_start => { + self.bracketed_paste = true; + self.bracketed_paste_buffer.clearRetainingCapacity(); + }, + .paste_end => { + defer self.bracketed_paste_buffer.clearAndFree(); + if (!self.bracketed_paste) return; + self.bracketed_paste = false; + if (self.dispatch_event) |f| f(self.handler_ctx, try self.fmtmsg(.{ "system_clipboard", self.bracketed_paste_buffer.items })); + }, + .cap_unicode => { + self.vx.caps.unicode = .unicode; + self.vx.screen.width_method = .unicode; + }, + .cap_da1 => { + std.Thread.Futex.wake(&self.vx.query_futex, 10); + }, + .cap_kitty_keyboard => { + self.vx.caps.kitty_keyboard = true; + }, + .cap_kitty_graphics => { + if (!self.vx.caps.kitty_graphics) { + self.vx.caps.kitty_graphics = true; + } + }, + .cap_rgb => { + self.vx.caps.rgb = true; + }, + } + } +} + +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; +} + +const OSC = "\x1B]"; // Operating System Command +const ST = "\x1B\\"; // String Terminator +const BEL = "\x07"; +const OSC0_title = OSC ++ "0;"; +const OSC52_clipboard = OSC ++ "52;c;"; +const OSC52_clipboard_paste = OSC ++ "52;p;"; +const OSC22_cursor = OSC ++ "22;"; +const OSC22_cursor_reply = OSC ++ "22:"; + +const CSI = "\x1B["; // Control Sequence Introducer +const CSI_bracketed_paste_enable = CSI ++ "?2004h"; +const CSI_bracketed_paste_disable = CSI ++ "?2004h"; +const CIS_bracketed_paste_begin = CSI ++ "200~"; +const CIS_bracketed_paste_end = CSI ++ "201~"; + +pub fn set_terminal_title(text: []const u8) void { + var writer = std.io.getStdOut().writer(); + var buf: [std.posix.PATH_MAX]u8 = undefined; + const term_cmd = std.fmt.bufPrint(&buf, OSC0_title ++ "{s}" ++ BEL, .{text}) catch return; + _ = writer.write(term_cmd) catch return; +} + +pub fn copy_to_system_clipboard(tmp_a: std.mem.Allocator, text: []const u8) void { + copy_to_system_clipboard_with_errors(tmp_a, text) catch |e| log.logger(log_name).err("copy_to_system_clipboard", e); +} + +fn copy_to_system_clipboard_with_errors(tmp_a: std.mem.Allocator, text: []const u8) !void { + var writer = std.io.getStdOut().writer(); + const encoder = std.base64.standard.Encoder; + const size = OSC52_clipboard.len + encoder.calcSize(text.len) + ST.len; + const buf = try tmp_a.alloc(u8, size); + defer tmp_a.free(buf); + @memcpy(buf[0..OSC52_clipboard.len], OSC52_clipboard); + const b64 = encoder.encode(buf[OSC52_clipboard.len..], text); + @memcpy(buf[OSC52_clipboard.len + b64.len ..], ST); + _ = try writer.write(buf); +} + +pub fn request_system_clipboard() void { + write_stdout(OSC52_clipboard ++ "?" ++ ST); +} + +pub fn request_mouse_cursor_text(push_or_pop: bool) void { + if (push_or_pop) mouse_cursor_push("text") else mouse_cursor_pop(); +} + +pub fn request_mouse_cursor_pointer(push_or_pop: bool) void { + if (push_or_pop) mouse_cursor_push("pointer") else mouse_cursor_pop(); +} + +pub fn request_mouse_cursor_default(push_or_pop: bool) void { + if (push_or_pop) mouse_cursor_push("default") else mouse_cursor_pop(); +} + +fn mouse_cursor_push(comptime name: []const u8) void { + write_stdout(OSC22_cursor ++ name ++ ST); +} + +fn mouse_cursor_pop() void { + write_stdout(OSC22_cursor ++ "default" ++ ST); +} + +fn write_stdout(bytes: []const u8) void { + _ = std.io.getStdOut().writer().write(bytes) catch |e| log.logger(log_name).err("stdout", e); +} + +pub fn cursor_enable(self: *Self, y: c_int, x: c_int) !void { + self.vx.screen.cursor_vis = true; + self.vx.screen.cursor_row = @intCast(y); + self.vx.screen.cursor_col = @intCast(x); +} + +pub fn cursor_disable(self: *Self) void { + self.vx.screen.cursor_vis = false; +} + +pub fn ucs32_to_utf8(ucs32: []const u32, utf8: []u8) !usize { + return @intCast(try std.unicode.utf8Encode(@intCast(ucs32[0]), utf8)); +} diff --git a/src/renderer/vaxis/style.zig b/src/renderer/vaxis/style.zig new file mode 100644 index 0000000..4420bd5 --- /dev/null +++ b/src/renderer/vaxis/style.zig @@ -0,0 +1,14 @@ +pub const StyleBits = packed struct(u5) { + struck: bool = false, + bold: bool = false, + 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 = .{}; +}; diff --git a/src/tui/WidgetList.zig b/src/tui/WidgetList.zig index 66b4262..16d54db 100644 --- a/src/tui/WidgetList.zig +++ b/src/tui/WidgetList.zig @@ -33,21 +33,21 @@ on_resize: *const fn (ctx: ?*anyopaque, self: *Self, pos_: Widget.Box) void = on pub fn createH(a: Allocator, parent: Widget, name: [:0]const u8, layout_: Layout) !*Self { const self: *Self = try a.create(Self); self.* = try init(a, parent, name, .horizontal, layout_, Box{}); - self.plane.move_bottom(); + self.plane.hide(); return self; } pub fn createV(a: Allocator, parent: Widget, name: [:0]const u8, layout_: Layout) !*Self { const self: *Self = try a.create(Self); self.* = try init(a, parent, name, .vertical, layout_, Box{}); - self.plane.move_bottom(); + self.plane.hide(); return self; } pub fn createBox(a: Allocator, parent: Widget, name: [:0]const u8, dir: Direction, layout_: Layout, box: Box) !*Self { const self: *Self = try a.create(Self); self.* = try init(a, parent, name, dir, layout_, box); - self.plane.move_bottom(); + self.plane.hide(); return self; } diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 05fbcec..7a7d94b 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -104,13 +104,13 @@ pub const CurSel = struct { }; } - fn expand_selection_to_line(self: *Self, root: Buffer.Root) *Selection { + fn expand_selection_to_line(self: *Self, root: Buffer.Root, plane: Plane) *Selection { const sel = self.enable_selection(); sel.normalize(); sel.begin.move_begin(); if (!(sel.end.row > sel.begin.row and sel.end.col == 0)) { - sel.end.move_end(root); - sel.end.move_right(root) catch {}; + sel.end.move_end(root, plane); + sel.end.move_right(root, plane) catch {}; } return sel; } @@ -365,6 +365,8 @@ pub const Editor = struct { } pub fn handle_resize(self: *Self, pos: Widget.Box) void { + self.plane.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return; + self.plane.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return; self.view.rows = pos.h; self.view.cols = pos.w; } @@ -539,10 +541,10 @@ pub const Editor = struct { } } - fn find_first_non_ws(root: Buffer.Root, row: usize) usize { + fn find_first_non_ws(root: Buffer.Root, row: usize, plane: Plane) usize { const Ctx = struct { col: usize = 0, - fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize) Buffer.Walker { + fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Plane) Buffer.Walker { const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); if (egc[0] == ' ' or egc[0] == '\t') { ctx.col += wcwidth; @@ -552,11 +554,19 @@ pub const Editor = struct { } }; var ctx: Ctx = .{}; - root.walk_egc_forward(row, Ctx.walker, &ctx) catch return 0; + root.walk_egc_forward(row, Ctx.walker, &ctx, plane) catch return 0; return ctx.col; } - fn write_range(self: *const Self, root: Buffer.Root, sel: Selection, writer: anytype, map_error: fn (e: anyerror) @TypeOf(writer).Error, wcwidth_: ?*usize) @TypeOf(writer).Error!void { + fn write_range( + self: *const Self, + root: Buffer.Root, + sel: Selection, + writer: anytype, + map_error: fn (e: anyerror) @TypeOf(writer).Error, + wcwidth_: ?*usize, + plane_: Plane, + ) @TypeOf(writer).Error!void { _ = self; const Writer = @TypeOf(writer); const Ctx = struct { @@ -564,7 +574,7 @@ pub const Editor = struct { sel: Selection, writer: Writer, wcwidth: usize = 0, - fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize) Buffer.Walker { + fn walker(ctx_: *anyopaque, egc: []const u8, wcwidth: usize, _: Plane) Buffer.Walker { const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); if (ctx.col < ctx.sel.begin.col) { ctx.col += wcwidth; @@ -591,7 +601,7 @@ pub const Editor = struct { ctx.sel.normalize(); if (sel.begin.eql(sel.end)) return; - root.walk_egc_forward(sel.begin.row, Ctx.walker, &ctx) catch |e| return map_error(e); + root.walk_egc_forward(sel.begin.row, Ctx.walker, &ctx, plane_) catch |e| return map_error(e); if (wcwidth_) |p| p.* = ctx.wcwidth; } @@ -622,20 +632,20 @@ pub const Editor = struct { return self.scroll_dest != self.view.row; } - fn render_screen(self: *const Self, theme: *const Widget.Theme, cache: *StyleCache) void { + fn render_screen(self: *Self, theme: *const Widget.Theme, cache: *StyleCache) void { const ctx = struct { - self: *const Self, + self: *Self, buf_row: usize, buf_col: usize = 0, match_idx: usize = 0, theme: *const Widget.Theme, hl_row: ?usize, - fn walker(ctx_: *anyopaque, leaf: *const Buffer.Leaf) Buffer.Walker { + fn walker(ctx_: *anyopaque, leaf: *const Buffer.Leaf, _: Plane) Buffer.Walker { const ctx = @as(*@This(), @ptrCast(@alignCast(ctx_))); const self_ = ctx.self; const view = self_.view; - const n = self_.plane; + const n = &self_.plane; if (ctx.buf_row > view.row + view.rows) return Buffer.Walker.stop; @@ -724,7 +734,7 @@ pub const Editor = struct { if (hl_row) |_| self.render_line_highlight(&self.get_primary().cursor, theme) catch {}; self.plane.home(); - _ = root.walk_from_line_begin_const(self.view.row, ctx.walker, &ctx_) catch {}; + _ = root.walk_from_line_begin_const(self.view.row, ctx.walker, &ctx_, self.plane) catch {}; } self.render_syntax(theme, cache, root) catch {}; self.render_diagnostics(theme, hl_row) catch {}; @@ -733,16 +743,14 @@ pub const Editor = struct { fn render_terminal_cursor(self: *const Self, cursor_: *const Cursor) !void { if (self.screen_cursor(cursor_)) |cursor| { - var y: c_int = @intCast(cursor.row); - var x: c_int = @intCast(cursor.col); - self.plane.rel_yx_to_abs(&y, &x); + const y, const x = self.plane.rel_yx_to_abs(@intCast(cursor.row), @intCast(cursor.col)); tui.current().rdr.cursor_enable(y, x) catch {}; } else { tui.current().rdr.cursor_disable(); } } - fn render_cursors(self: *const Self, theme: *const Widget.Theme) !void { + fn render_cursors(self: *Self, theme: *const Widget.Theme) !void { const frame = tracy.initZone(@src(), .{ .name = "editor render cursors" }); defer frame.deinit(); for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| @@ -751,14 +759,14 @@ pub const Editor = struct { try self.render_terminal_cursor(&self.get_primary().cursor); } - fn render_cursor(self: *const Self, cursor: *const Cursor, theme: *const Widget.Theme) !void { + fn render_cursor(self: *Self, cursor: *const Cursor, theme: *const Widget.Theme) !void { if (self.screen_cursor(cursor)) |pos| { self.plane.cursor_move_yx(@intCast(pos.row), @intCast(pos.col)) catch return; self.render_cursor_cell(theme); } } - fn render_line_highlight(self: *const Self, cursor: *const Cursor, theme: *const Widget.Theme) !void { + fn render_line_highlight(self: *Self, cursor: *const Cursor, theme: *const Widget.Theme) !void { const row_min = self.view.row; const row_max = row_min + self.view.rows; if (cursor.row < row_min or row_max < cursor.row) @@ -806,11 +814,11 @@ pub const Editor = struct { }; } - fn render_diagnostics(self: *const Self, theme: *const Widget.Theme, hl_row: ?usize) !void { + fn render_diagnostics(self: *Self, theme: *const Widget.Theme, hl_row: ?usize) !void { for (self.diagnostics.items) |*diag| self.render_diagnostic(diag, theme, hl_row); } - fn render_diagnostic(self: *const Self, diag: *const Diagnostic, theme: *const Widget.Theme, hl_row: ?usize) void { + fn render_diagnostic(self: *Self, diag: *const Diagnostic, theme: *const Widget.Theme, hl_row: ?usize) void { const screen_width = self.view.cols; const pos = self.screen_cursor(&diag.sel.begin) orelse return; var style = switch (diag.get_severity()) { @@ -832,13 +840,13 @@ pub const Editor = struct { self.render_diagnostic_cell(style); } } - const space_begin = get_line_end_space_begin(self.plane, screen_width, pos.row); + const space_begin = get_line_end_space_begin(&self.plane, screen_width, pos.row); if (space_begin < screen_width) { self.render_diagnostic_message(diag.message, pos.row, screen_width - space_begin, style); } } - fn get_line_end_space_begin(plane: Plane, screen_width: usize, screen_row: usize) usize { + fn get_line_end_space_begin(plane: *Plane, screen_width: usize, screen_row: usize) usize { var pos = screen_width; var cell = plane.cell_init(); while (pos > 0) : (pos -= 1) { @@ -849,19 +857,19 @@ pub const Editor = struct { return pos; } - fn render_diagnostic_message(self: *const Self, message: []const u8, y: usize, max_space: usize, style: Widget.Theme.Style) void { + fn render_diagnostic_message(self: *Self, message: []const u8, y: usize, max_space: usize, style: Widget.Theme.Style) void { self.plane.set_style(style); _ = self.plane.print_aligned_right(@intCast(y), "{s}", .{message[0..@min(max_space, message.len)]}) catch {}; } - inline fn render_diagnostic_cell(self: *const Self, _: Widget.Theme.Style) void { + inline fn render_diagnostic_cell(self: *Self, _: Widget.Theme.Style) void { var cell = self.plane.cell_init(); _ = self.plane.at_cursor_cell(&cell) catch return; cell.set_style(.{ .fs = .undercurl }); _ = self.plane.putc(&cell) catch {}; } - inline fn render_cursor_cell(self: *const Self, theme: *const Widget.Theme) void { + inline fn render_cursor_cell(self: *Self, theme: *const Widget.Theme) void { var cell = self.plane.cell_init(); _ = self.plane.at_cursor_cell(&cell) catch return; cell.set_style(theme.editor_cursor); @@ -880,7 +888,7 @@ pub const Editor = struct { cell.set_style_bg(theme.editor_line_highlight); } - inline fn render_control_code(self: *const Self, c: *Cell, n: Plane, code: u8, theme: *const Widget.Theme) struct { usize, usize } { + inline fn render_control_code(self: *const Self, c: *Cell, n: *Plane, code: u8, theme: *const Widget.Theme) struct { usize, usize } { const val = Buffer.unicode.control_code_to_unicode(code); if (self.show_whitespace) c.set_style(theme.editor_whitespace); @@ -888,7 +896,7 @@ pub const Editor = struct { return .{ 1, 1 }; } - inline fn render_eol(self: *const Self, n: Plane, theme: *const Widget.Theme) Cell { + inline fn render_eol(self: *const Self, n: *Plane, theme: *const Widget.Theme) Cell { var cell = n.cell_init(); const c = &cell; if (self.show_whitespace) { @@ -913,14 +921,14 @@ pub const Editor = struct { return cell; } - inline fn render_terminator(n: Plane, theme: *const Widget.Theme) Cell { + inline fn render_terminator(n: *Plane, theme: *const Widget.Theme) Cell { var cell = n.cell_init(); cell.set_style(theme.editor); _ = n.cell_load(&cell, "\u{2003}") catch unreachable; return cell; } - inline fn render_space(self: *const Self, c: *Cell, n: Plane, theme: *const Widget.Theme) struct { usize, usize } { + inline fn render_space(self: *const Self, c: *Cell, n: *Plane, theme: *const Widget.Theme) struct { usize, usize } { if (self.show_whitespace) { c.set_style(theme.editor_whitespace); _ = n.cell_load(c, "·") catch {}; @@ -941,7 +949,7 @@ pub const Editor = struct { return .{ 1, 1 }; } - inline fn render_tab(self: *const Self, c: *Cell, n: Plane, abs_col: usize, theme: *const Widget.Theme) struct { usize, usize } { + inline fn render_tab(self: *const Self, c: *Cell, n: *Plane, abs_col: usize, theme: *const Widget.Theme) struct { usize, usize } { if (self.show_whitespace) { c.set_style(theme.editor_whitespace); _ = n.cell_load(c, "→") catch {}; @@ -952,18 +960,18 @@ pub const Editor = struct { return .{ 1, 9 - (abs_col % 8) }; } - inline fn render_egc(c: *Cell, n: Plane, egc: [:0]const u8) struct { usize, usize } { + inline fn render_egc(c: *Cell, n: *Plane, egc: [:0]const u8) struct { usize, usize } { const bytes = n.cell_load(c, egc) catch return .{ 1, 1 }; const colcount = c.columns(); return .{ bytes, colcount }; } - fn render_syntax(self: *const Self, theme: *const Widget.Theme, cache: *StyleCache, root: Buffer.Root) !void { + fn render_syntax(self: *Self, theme: *const Widget.Theme, cache: *StyleCache, root: Buffer.Root) !void { const frame = tracy.initZone(@src(), .{ .name = "editor render syntax" }); defer frame.deinit(); const syn = if (self.syntax) |syn| syn else return; const Ctx = struct { - self: *const Self, + self: *Self, theme: *const Widget.Theme, cache: *StyleCache, last_row: usize = std.math.maxInt(usize), @@ -971,7 +979,7 @@ pub const Editor = struct { root: Buffer.Root, pos_cache: PosToWidthCache, fn cb(ctx: *@This(), range: syntax.Range, scope: []const u8, id: u32, _: usize, _: *const syntax.Node) error{Stop}!void { - const sel_ = ctx.pos_cache.range_to_selection(range, ctx.root) orelse return; + const sel_ = ctx.pos_cache.range_to_selection(range, ctx.root, ctx.self.plane) orelse return; defer { ctx.last_row = sel_.begin.row; ctx.last_col = sel_.begin.col; @@ -1260,38 +1268,38 @@ pub const Editor = struct { self.match_token += 1; } - fn with_cursor_const(root: Buffer.Root, move: cursor_operator_const, cursel: *CurSel) error{Stop}!void { - try move(root, &cursel.cursor); + fn with_cursor_const(root: Buffer.Root, move: cursor_operator_const, cursel: *CurSel, plane: Plane) error{Stop}!void { + try move(root, &cursel.cursor, plane); } fn with_cursors_const(self: *Self, root: Buffer.Root, move: cursor_operator_const) error{Stop}!void { for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { cursel.selection = null; - try with_cursor_const(root, move, cursel); + try with_cursor_const(root, move, cursel, self.plane); }; self.collapse_cursors(); } - fn with_cursor_const_arg(root: Buffer.Root, move: cursor_operator_const_arg, cursel: *CurSel, ctx: command.Context) error{Stop}!void { - try move(root, &cursel.cursor, ctx); + fn with_cursor_const_arg(root: Buffer.Root, move: cursor_operator_const_arg, cursel: *CurSel, ctx: command.Context, plane: Plane) error{Stop}!void { + try move(root, &cursel.cursor, ctx, plane); } fn with_cursors_const_arg(self: *Self, root: Buffer.Root, move: cursor_operator_const_arg, ctx: command.Context) error{Stop}!void { for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { cursel.selection = null; - try with_cursor_const_arg(root, move, cursel, ctx); + try with_cursor_const_arg(root, move, cursel, ctx, self.plane); }; self.collapse_cursors(); } - fn with_cursor_and_view_const(root: Buffer.Root, move: cursor_view_operator_const, cursel: *CurSel, view: *const View) error{Stop}!void { - try move(root, &cursel.cursor, view); + fn with_cursor_and_view_const(root: Buffer.Root, move: cursor_view_operator_const, cursel: *CurSel, view: *const View, plane: Plane) error{Stop}!void { + try move(root, &cursel.cursor, view, plane); } fn with_cursors_and_view_const(self: *Self, root: Buffer.Root, move: cursor_view_operator_const, view: *const View) error{Stop}!void { var someone_stopped = false; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| - with_cursor_and_view_const(root, move, cursel, view) catch { + with_cursor_and_view_const(root, move, cursel, view, self.plane) catch { someone_stopped = true; }; self.collapse_cursors(); @@ -1312,9 +1320,9 @@ pub const Editor = struct { return root; } - fn with_selection_const(root: Buffer.Root, move: cursor_operator_const, cursel: *CurSel) error{Stop}!void { + fn with_selection_const(root: Buffer.Root, move: cursor_operator_const, cursel: *CurSel, plane: Plane) error{Stop}!void { const sel = cursel.enable_selection(); - try move(root, &sel.end); + try move(root, &sel.end, plane); cursel.cursor = sel.end; cursel.check_selection(); } @@ -1322,16 +1330,16 @@ pub const Editor = struct { fn with_selections_const(self: *Self, root: Buffer.Root, move: cursor_operator_const) error{Stop}!void { var someone_stopped = false; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| - with_selection_const(root, move, cursel) catch { + with_selection_const(root, move, cursel, self.plane) catch { someone_stopped = true; }; self.collapse_cursors(); return if (someone_stopped) error.Stop else {}; } - fn with_selection_const_arg(root: Buffer.Root, move: cursor_operator_const_arg, cursel: *CurSel, ctx: command.Context) error{Stop}!void { + fn with_selection_const_arg(root: Buffer.Root, move: cursor_operator_const_arg, cursel: *CurSel, ctx: command.Context, plane: Plane) error{Stop}!void { const sel = cursel.enable_selection(); - try move(root, &sel.end, ctx); + try move(root, &sel.end, ctx, plane); cursel.cursor = sel.end; cursel.check_selection(); } @@ -1339,23 +1347,23 @@ pub const Editor = struct { fn with_selections_const_arg(self: *Self, root: Buffer.Root, move: cursor_operator_const_arg, ctx: command.Context) error{Stop}!void { var someone_stopped = false; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| - with_selection_const_arg(root, move, cursel, ctx) catch { + with_selection_const_arg(root, move, cursel, ctx, self.plane) catch { someone_stopped = true; }; self.collapse_cursors(); return if (someone_stopped) error.Stop else {}; } - fn with_selection_and_view_const(root: Buffer.Root, move: cursor_view_operator_const, cursel: *CurSel, view: *const View) error{Stop}!void { + fn with_selection_and_view_const(root: Buffer.Root, move: cursor_view_operator_const, cursel: *CurSel, view: *const View, plane: Plane) error{Stop}!void { const sel = cursel.enable_selection(); - try move(root, &sel.end, view); + try move(root, &sel.end, view, plane); cursel.cursor = sel.end; } fn with_selections_and_view_const(self: *Self, root: Buffer.Root, move: cursor_view_operator_const, view: *const View) error{Stop}!void { var someone_stopped = false; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| - with_selection_and_view_const(root, move, cursel, view) catch { + with_selection_and_view_const(root, move, cursel, view, self.plane) catch { someone_stopped = true; }; self.collapse_cursors(); @@ -1415,7 +1423,7 @@ pub const Editor = struct { if (self.syntax) |syn| { const root = self.buf_root() catch return; var start_byte: usize = 0; - _ = root.get_range(.{ .begin = .{}, .end = nudge.begin }, null, &start_byte, null) catch return; + _ = root.get_range(.{ .begin = .{}, .end = nudge.begin }, null, &start_byte, null, self.plane) catch return; syn.edit(.{ .start_byte = @intCast(start_byte), .old_end_byte = @intCast(start_byte), @@ -1447,7 +1455,7 @@ pub const Editor = struct { cursel.cursor = sel.begin; cursel.selection = null; var size: usize = 0; - const root_ = try root.delete_range(sel, a, &size); + const root_ = try root.delete_range(sel, a, &size, self.plane); self.nudge_delete(sel, cursel, size); return root_; } @@ -1461,7 +1469,7 @@ pub const Editor = struct { all_stop = false; continue; } - with_selection_const(root, move, cursel) catch continue; + with_selection_const(root, move, cursel, self.plane) catch continue; root = self.delete_selection(root, cursel, a) catch continue; all_stop = false; }; @@ -1471,10 +1479,10 @@ pub const Editor = struct { return root; } - const cursor_predicate = *const fn (root: Buffer.Root, cursor: *Cursor) bool; - const cursor_operator_const = *const fn (root: Buffer.Root, cursor: *Cursor) error{Stop}!void; - const cursor_operator_const_arg = *const fn (root: Buffer.Root, cursor: *Cursor, ctx: command.Context) error{Stop}!void; - const cursor_view_operator_const = *const fn (root: Buffer.Root, cursor: *Cursor, view: *const View) error{Stop}!void; + const cursor_predicate = *const fn (root: Buffer.Root, cursor: *Cursor, plane: Plane) bool; + const cursor_operator_const = *const fn (root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void; + const cursor_operator_const_arg = *const fn (root: Buffer.Root, cursor: *Cursor, ctx: command.Context, plane: Plane) error{Stop}!void; + const cursor_view_operator_const = *const fn (root: Buffer.Root, cursor: *Cursor, view: *const View, plane: Plane) error{Stop}!void; const cursel_operator_const = *const fn (root: Buffer.Root, cursel: *CurSel) error{Stop}!void; const cursor_operator = *const fn (root: Buffer.Root, cursor: *Cursor, a: Allocator) error{Stop}!Buffer.Root; const cursel_operator = *const fn (root: Buffer.Root, cursel: *CurSel, a: Allocator) error{Stop}!Buffer.Root; @@ -1512,148 +1520,148 @@ pub const Editor = struct { return !is_not_word_char(c); } - fn is_word_char_at_cursor(root: Buffer.Root, cursor: *const Cursor) bool { - return cursor.test_at(root, is_word_char); + fn is_word_char_at_cursor(root: Buffer.Root, cursor: *const Cursor, plane: Plane) bool { + return cursor.test_at(root, is_word_char, plane); } - fn is_non_word_char_at_cursor(root: Buffer.Root, cursor: *const Cursor) bool { - return cursor.test_at(root, is_not_word_char); + fn is_non_word_char_at_cursor(root: Buffer.Root, cursor: *const Cursor, plane: Plane) bool { + return cursor.test_at(root, is_not_word_char, plane); } - fn is_word_boundary_left(root: Buffer.Root, cursor: *const Cursor) bool { + fn is_word_boundary_left(root: Buffer.Root, cursor: *const Cursor, plane: Plane) bool { if (cursor.col == 0) return true; - if (is_non_word_char_at_cursor(root, cursor)) + if (is_non_word_char_at_cursor(root, cursor, plane)) return false; var next = cursor.*; - next.move_left(root) catch return true; - if (is_non_word_char_at_cursor(root, &next)) + next.move_left(root, plane) catch return true; + if (is_non_word_char_at_cursor(root, &next, plane)) return true; return false; } - fn is_non_word_boundary_left(root: Buffer.Root, cursor: *const Cursor) bool { + fn is_non_word_boundary_left(root: Buffer.Root, cursor: *const Cursor, plane: Plane) bool { if (cursor.col == 0) return true; - if (is_word_char_at_cursor(root, cursor)) + if (is_word_char_at_cursor(root, cursor, plane)) return false; var next = cursor.*; - next.move_left(root) catch return true; - if (is_word_char_at_cursor(root, &next)) + next.move_left(root, plane) catch return true; + if (is_word_char_at_cursor(root, &next, plane)) return true; return false; } - fn is_word_boundary_right(root: Buffer.Root, cursor: *const Cursor) bool { - const line_width = root.line_width(cursor.row) catch return true; + fn is_word_boundary_right(root: Buffer.Root, cursor: *const Cursor, plane: Plane) bool { + const line_width = root.line_width(cursor.row, plane) catch return true; if (cursor.col >= line_width) return true; - if (is_non_word_char_at_cursor(root, cursor)) + if (is_non_word_char_at_cursor(root, cursor, plane)) return false; var next = cursor.*; - next.move_right(root) catch return true; - if (is_non_word_char_at_cursor(root, &next)) + next.move_right(root, plane) catch return true; + if (is_non_word_char_at_cursor(root, &next, plane)) return true; return false; } - fn is_non_word_boundary_right(root: Buffer.Root, cursor: *const Cursor) bool { - const line_width = root.line_width(cursor.row) catch return true; + fn is_non_word_boundary_right(root: Buffer.Root, cursor: *const Cursor, plane: Plane) bool { + const line_width = root.line_width(cursor.row, plane) catch return true; if (cursor.col >= line_width) return true; - if (is_word_char_at_cursor(root, cursor)) + if (is_word_char_at_cursor(root, cursor, plane)) return false; var next = cursor.*; - next.move_right(root) catch return true; - if (is_word_char_at_cursor(root, &next)) + next.move_right(root, plane) catch return true; + if (is_word_char_at_cursor(root, &next, plane)) return true; return false; } - fn is_eol_left(_: Buffer.Root, cursor: *const Cursor) bool { + fn is_eol_left(_: Buffer.Root, cursor: *const Cursor, _: Plane) bool { if (cursor.col == 0) return true; return false; } - fn is_eol_right(root: Buffer.Root, cursor: *const Cursor) bool { - const line_width = root.line_width(cursor.row) catch return true; + fn is_eol_right(root: Buffer.Root, cursor: *const Cursor, plane: Plane) bool { + const line_width = root.line_width(cursor.row, plane) catch return true; if (cursor.col >= line_width) return true; return false; } - fn is_eol_right_vim(root: Buffer.Root, cursor: *const Cursor) bool { - const line_width = root.line_width(cursor.row) catch return true; + fn is_eol_right_vim(root: Buffer.Root, cursor: *const Cursor, plane: Plane) bool { + const line_width = root.line_width(cursor.row, plane) catch return true; if (line_width == 0) return true; if (cursor.col >= line_width - 1) return true; return false; } - fn move_cursor_left(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - try cursor.move_left(root); + fn move_cursor_left(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + try cursor.move_left(root, plane); } - fn move_cursor_left_until(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate) void { - while (!pred(root, cursor)) - move_cursor_left(root, cursor) catch return; + fn move_cursor_left_until(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, plane: Plane) void { + while (!pred(root, cursor, plane)) + move_cursor_left(root, cursor, plane) catch return; } - fn move_cursor_left_unless(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate) void { - if (!pred(root, cursor)) - move_cursor_left(root, cursor) catch return; + fn move_cursor_left_unless(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, plane: Plane) void { + if (!pred(root, cursor, plane)) + move_cursor_left(root, cursor, plane) catch return; } - fn move_cursor_begin(_: Buffer.Root, cursor: *Cursor) !void { + fn move_cursor_begin(_: Buffer.Root, cursor: *Cursor, _: Plane) !void { cursor.move_begin(); } - fn smart_move_cursor_begin(root: Buffer.Root, cursor: *Cursor) !void { - const first = find_first_non_ws(root, cursor.row); - return if (cursor.col == first) cursor.move_begin() else cursor.move_to(root, cursor.row, first); + fn smart_move_cursor_begin(root: Buffer.Root, cursor: *Cursor, plane: Plane) !void { + const first = find_first_non_ws(root, cursor.row, plane); + return if (cursor.col == first) cursor.move_begin() else cursor.move_to(root, cursor.row, first, plane); } - fn move_cursor_right(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - try cursor.move_right(root); + fn move_cursor_right(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + try cursor.move_right(root, plane); } - fn move_cursor_right_until(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate) void { - while (!pred(root, cursor)) - move_cursor_right(root, cursor) catch return; + fn move_cursor_right_until(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, plane: Plane) void { + while (!pred(root, cursor, plane)) + move_cursor_right(root, cursor, plane) catch return; } - fn move_cursor_right_unless(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate) void { - if (!pred(root, cursor)) - move_cursor_right(root, cursor) catch return; + fn move_cursor_right_unless(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, plane: Plane) void { + if (!pred(root, cursor, plane)) + move_cursor_right(root, cursor, plane) catch return; } - fn move_cursor_end(root: Buffer.Root, cursor: *Cursor) !void { - cursor.move_end(root); + fn move_cursor_end(root: Buffer.Root, cursor: *Cursor, plane: Plane) !void { + cursor.move_end(root, plane); } - fn move_cursor_up(root: Buffer.Root, cursor: *Cursor) !void { - try cursor.move_up(root); + fn move_cursor_up(root: Buffer.Root, cursor: *Cursor, plane: Plane) !void { + try cursor.move_up(root, plane); } - fn move_cursor_down(root: Buffer.Root, cursor: *Cursor) !void { - try cursor.move_down(root); + fn move_cursor_down(root: Buffer.Root, cursor: *Cursor, plane: Plane) !void { + try cursor.move_down(root, plane); } - fn move_cursor_buffer_begin(_: Buffer.Root, cursor: *Cursor) !void { + fn move_cursor_buffer_begin(_: Buffer.Root, cursor: *Cursor, _: Plane) !void { cursor.move_buffer_begin(); } - fn move_cursor_buffer_end(root: Buffer.Root, cursor: *Cursor) !void { - cursor.move_buffer_end(root); + fn move_cursor_buffer_end(root: Buffer.Root, cursor: *Cursor, plane: Plane) !void { + cursor.move_buffer_end(root, plane); } - fn move_cursor_page_up(root: Buffer.Root, cursor: *Cursor, view: *const View) !void { - cursor.move_page_up(root, view); + fn move_cursor_page_up(root: Buffer.Root, cursor: *Cursor, view: *const View, plane: Plane) !void { + cursor.move_page_up(root, view, plane); } - fn move_cursor_page_down(root: Buffer.Root, cursor: *Cursor, view: *const View) !void { - cursor.move_page_down(root, view); + fn move_cursor_page_down(root: Buffer.Root, cursor: *Cursor, view: *const View, plane: Plane) !void { + cursor.move_page_down(root, view, plane); } pub fn primary_click(self: *Self, y: c_int, x: c_int) tp.result { @@ -1666,7 +1674,7 @@ pub const Editor = struct { self.selection_mode = .char; try self.send_editor_jump_source(); const root = self.buf_root() catch return; - primary.cursor.move_abs(root, &self.view, @intCast(y), @intCast(x)) catch return; + primary.cursor.move_abs(root, &self.view, @intCast(y), @intCast(x), self.plane) catch return; self.clamp_mouse(); try self.send_editor_jump_destination(); if (self.jump_mode) try self.goto_definition(.{}); @@ -1677,7 +1685,7 @@ pub const Editor = struct { primary.selection = null; self.selection_mode = .word; const root = self.buf_root() catch return; - primary.cursor.move_abs(root, &self.view, @intCast(y), @intCast(x)) catch return; + primary.cursor.move_abs(root, &self.view, @intCast(y), @intCast(x), self.plane) catch return; _ = self.select_word_at_cursor(primary) catch |e| return tp.exit_error(e); self.clamp_mouse(); } @@ -1687,7 +1695,7 @@ pub const Editor = struct { primary.selection = null; self.selection_mode = .line; const root = self.buf_root() catch return; - primary.cursor.move_abs(root, &self.view, @intCast(y), @intCast(x)) catch return; + primary.cursor.move_abs(root, &self.view, @intCast(y), @intCast(x), self.plane) catch return; self.select_line_at_cursor(primary) catch |e| return tp.exit_error(e); self.clamp_mouse(); } @@ -1698,18 +1706,18 @@ pub const Editor = struct { const primary = self.get_primary(); const sel = primary.enable_selection(); const root = self.buf_root() catch return; - sel.end.move_abs(root, &self.view, @intCast(y_), @intCast(x_)) catch return; + sel.end.move_abs(root, &self.view, @intCast(y_), @intCast(x_), self.plane) catch return; switch (self.selection_mode) { .char => {}, .word => if (sel.begin.right_of(sel.end)) - with_selection_const(root, move_cursor_word_begin, primary) catch return + with_selection_const(root, move_cursor_word_begin, primary, self.plane) catch return else - with_selection_const(root, move_cursor_word_end, primary) catch return, + with_selection_const(root, move_cursor_word_end, primary, self.plane) catch return, .line => if (sel.begin.right_of(sel.end)) - with_selection_const(root, move_cursor_begin, primary) catch return + with_selection_const(root, move_cursor_begin, primary, self.plane) catch return else { - with_selection_const(root, move_cursor_end, primary) catch return; - with_selection_const(root, move_cursor_right, primary) catch return; + with_selection_const(root, move_cursor_end, primary, self.plane) catch return; + with_selection_const(root, move_cursor_right, primary, self.plane) catch return; }, } primary.cursor = sel.end; @@ -1854,29 +1862,29 @@ pub const Editor = struct { tui.renderer.copy_to_system_clipboard(self.a, text); } - fn copy_selection(root: Buffer.Root, sel: Selection, text_a: Allocator) ![]const u8 { + fn copy_selection(root: Buffer.Root, sel: Selection, text_a: Allocator, plane: Plane) ![]const u8 { var size: usize = 0; - _ = try root.get_range(sel, null, &size, null); + _ = try root.get_range(sel, null, &size, null, plane); const buf__ = try text_a.alloc(u8, size); - return (try root.get_range(sel, buf__, null, null)).?; + return (try root.get_range(sel, buf__, null, null, plane)).?; } pub fn get_selection(self: *const Self, sel: Selection, text_a: Allocator) ![]const u8 { - return copy_selection(try self.buf_root(), sel, text_a); + return copy_selection(try self.buf_root(), sel, text_a, self.plane); } fn copy_word_at_cursor(self: *Self, text_a: Allocator) ![]const u8 { const root = self.buf_root() catch |e| return tp.exit_error(e); const primary = self.get_primary(); const sel = if (primary.selection) |*sel| sel else self.select_word_at_cursor(primary) catch |e| return tp.exit_error(e); - return copy_selection(root, sel.*, text_a) catch |e| return tp.exit_error(e); + return copy_selection(root, sel.*, text_a, self.plane) catch |e| return tp.exit_error(e); } pub fn cut_selection(self: *Self, root: Buffer.Root, cursel: *CurSel) !struct { []const u8, Buffer.Root } { return if (cursel.selection) |sel| ret: { var old_selection: Selection = sel; old_selection.normalize(); - const cut_text = try copy_selection(root, sel, self.a); + const cut_text = try copy_selection(root, sel, self.a, self.plane); if (cut_text.len > 100) { self.logger.print("cut:{s}...", .{std.fmt.fmtSliceEscapeLower(cut_text[0..100])}); } else { @@ -1886,16 +1894,16 @@ pub const Editor = struct { } else error.Stop; } - fn expand_selection_to_all(root: Buffer.Root, sel: *Selection) !void { - try move_cursor_buffer_begin(root, &sel.begin); - try move_cursor_buffer_end(root, &sel.end); + fn expand_selection_to_all(root: Buffer.Root, sel: *Selection, plane: Plane) !void { + try move_cursor_buffer_begin(root, &sel.begin, plane); + try move_cursor_buffer_end(root, &sel.end, plane); } fn insert(self: *Self, root: Buffer.Root, cursel: *CurSel, s: []const u8, a: Allocator) !Buffer.Root { var root_ = if (cursel.selection) |_| try self.delete_selection(root, cursel, a) else root; const cursor = &cursel.cursor; const begin = cursel.cursor; - cursor.row, cursor.col, root_ = try root_.insert_chars(cursor.row, cursor.col, s, a); + cursor.row, cursor.col, root_ = try root_.insert_chars(cursor.row, cursor.col, s, a, self.plane); cursor.target = cursor.col; self.nudge_insert(.{ .begin = begin, .end = cursor.* }, cursel, s.len); return root_; @@ -1908,9 +1916,9 @@ pub const Editor = struct { if (self.cursels.items.len == 1) if (primary.selection) |_| {} else { const sel = primary.enable_selection(); - move_cursor_begin(root, &sel.begin) catch |e| return tp.exit_error(e); - move_cursor_end(root, &sel.end) catch |e| return tp.exit_error(e); - move_cursor_right(root, &sel.end) catch |e| return tp.exit_error(e); + move_cursor_begin(root, &sel.begin, self.plane) catch |e| return tp.exit_error(e); + move_cursor_end(root, &sel.end, self.plane) catch |e| return tp.exit_error(e); + move_cursor_right(root, &sel.end, self.plane) catch |e| return tp.exit_error(e); }; var first = true; var text = std.ArrayList(u8).init(self.a); @@ -1934,7 +1942,7 @@ pub const Editor = struct { var text = std.ArrayList(u8).init(self.a); for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { if (cursel.selection) |sel| { - const copy_text = copy_selection(root, sel, self.a) catch |e| return tp.exit_error(e); + const copy_text = copy_selection(root, sel, self.a, self.plane) catch |e| return tp.exit_error(e); if (first) { first = false; } else { @@ -2051,12 +2059,12 @@ pub const Editor = struct { self.clamp(); } - fn move_cursor_left_vim(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - move_cursor_left_unless(root, cursor, is_eol_left); + fn move_cursor_left_vim(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + move_cursor_left_unless(root, cursor, is_eol_left, plane); } - fn move_cursor_right_vim(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - move_cursor_right_unless(root, cursor, is_eol_right_vim); + fn move_cursor_right_vim(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + move_cursor_right_unless(root, cursor, is_eol_right_vim, plane); } pub fn move_left_vim(self: *Self, _: command.Context) tp.result { @@ -2071,63 +2079,63 @@ pub const Editor = struct { self.clamp(); } - fn move_cursor_word_begin(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - if (is_non_word_char_at_cursor(root, cursor)) { - move_cursor_left_until(root, cursor, is_word_boundary_right); - try move_cursor_right(root, cursor); + fn move_cursor_word_begin(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + if (is_non_word_char_at_cursor(root, cursor, plane)) { + move_cursor_left_until(root, cursor, is_word_boundary_right, plane); + try move_cursor_right(root, cursor, plane); } else { - move_cursor_left_until(root, cursor, is_word_boundary_left); + move_cursor_left_until(root, cursor, is_word_boundary_left, plane); } } - fn move_cursor_word_end(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - if (is_non_word_char_at_cursor(root, cursor)) { - move_cursor_right_until(root, cursor, is_word_boundary_left); - try move_cursor_left(root, cursor); + fn move_cursor_word_end(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + if (is_non_word_char_at_cursor(root, cursor, plane)) { + move_cursor_right_until(root, cursor, is_word_boundary_left, plane); + try move_cursor_left(root, cursor, plane); } else { - move_cursor_right_until(root, cursor, is_word_boundary_right); + move_cursor_right_until(root, cursor, is_word_boundary_right, plane); } - try move_cursor_right(root, cursor); + try move_cursor_right(root, cursor, plane); } - fn move_cursor_word_left(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - try move_cursor_left(root, cursor); - move_cursor_left_until(root, cursor, is_word_boundary_left); + fn move_cursor_word_left(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + try move_cursor_left(root, cursor, plane); + move_cursor_left_until(root, cursor, is_word_boundary_left, plane); } - fn move_cursor_word_left_space(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - try move_cursor_left(root, cursor); + fn move_cursor_word_left_space(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + try move_cursor_left(root, cursor, plane); var next = cursor.*; - next.move_left(root) catch - return move_cursor_left_until(root, cursor, is_word_boundary_left); - if (is_non_word_char_at_cursor(root, cursor) and is_non_word_char_at_cursor(root, &next)) - move_cursor_left_until(root, cursor, is_non_word_boundary_left) + next.move_left(root, plane) catch + return move_cursor_left_until(root, cursor, is_word_boundary_left, plane); + if (is_non_word_char_at_cursor(root, cursor, plane) and is_non_word_char_at_cursor(root, &next, plane)) + move_cursor_left_until(root, cursor, is_non_word_boundary_left, plane) else - move_cursor_left_until(root, cursor, is_word_boundary_left); + move_cursor_left_until(root, cursor, is_word_boundary_left, plane); } - pub fn move_cursor_word_right(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - move_cursor_right_until(root, cursor, is_word_boundary_right); - try move_cursor_right(root, cursor); + pub fn move_cursor_word_right(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + move_cursor_right_until(root, cursor, is_word_boundary_right, plane); + try move_cursor_right(root, cursor, plane); } - pub fn move_cursor_word_right_vim(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { - try move_cursor_right(root, cursor); - move_cursor_right_until(root, cursor, is_word_boundary_left); + pub fn move_cursor_word_right_vim(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { + try move_cursor_right(root, cursor, plane); + move_cursor_right_until(root, cursor, is_word_boundary_left, plane); } - pub fn move_cursor_word_right_space(root: Buffer.Root, cursor: *Cursor) error{Stop}!void { + pub fn move_cursor_word_right_space(root: Buffer.Root, cursor: *Cursor, plane: Plane) error{Stop}!void { var next = cursor.*; - next.move_right(root) catch { - move_cursor_right_until(root, cursor, is_word_boundary_right); - try move_cursor_right(root, cursor); + next.move_right(root, plane) catch { + move_cursor_right_until(root, cursor, is_word_boundary_right, plane); + try move_cursor_right(root, cursor, plane); return; }; - if (is_non_word_char_at_cursor(root, cursor) and is_non_word_char_at_cursor(root, &next)) - move_cursor_right_until(root, cursor, is_non_word_boundary_right) + if (is_non_word_char_at_cursor(root, cursor, plane) and is_non_word_char_at_cursor(root, &next, plane)) + move_cursor_right_until(root, cursor, is_non_word_boundary_right, plane) else - move_cursor_right_until(root, cursor, is_word_boundary_right); - try move_cursor_right(root, cursor); + move_cursor_right_until(root, cursor, is_word_boundary_right, plane); + try move_cursor_right(root, cursor, plane); } pub fn move_word_left(self: *Self, _: command.Context) tp.result { @@ -2148,33 +2156,33 @@ pub const Editor = struct { self.clamp(); } - fn move_cursor_to_char_left(root: Buffer.Root, cursor: *Cursor, ctx: command.Context) error{Stop}!void { + fn move_cursor_to_char_left(root: Buffer.Root, cursor: *Cursor, ctx: command.Context, plane: Plane) error{Stop}!void { var egc: []const u8 = undefined; if (!(ctx.args.match(.{tp.extract(&egc)}) catch return error.Stop)) return error.Stop; - try move_cursor_left(root, cursor); + try move_cursor_left(root, cursor, plane); while (true) { - const curr_egc, _, _ = root.ecg_at(cursor.row, cursor.col) catch return error.Stop; + const curr_egc, _, _ = root.ecg_at(cursor.row, cursor.col, plane) catch return error.Stop; if (std.mem.eql(u8, curr_egc, egc)) return; - if (is_eol_left(root, cursor)) + if (is_eol_left(root, cursor, plane)) return; - move_cursor_left(root, cursor) catch return error.Stop; + move_cursor_left(root, cursor, plane) catch return error.Stop; } } - pub fn move_cursor_to_char_right(root: Buffer.Root, cursor: *Cursor, ctx: command.Context) error{Stop}!void { + pub fn move_cursor_to_char_right(root: Buffer.Root, cursor: *Cursor, ctx: command.Context, plane: Plane) error{Stop}!void { var egc: []const u8 = undefined; if (!(ctx.args.match(.{tp.extract(&egc)}) catch return error.Stop)) return error.Stop; - try move_cursor_right(root, cursor); + try move_cursor_right(root, cursor, plane); while (true) { - const curr_egc, _, _ = root.ecg_at(cursor.row, cursor.col) catch return error.Stop; + const curr_egc, _, _ = root.ecg_at(cursor.row, cursor.col, plane) catch return error.Stop; if (std.mem.eql(u8, curr_egc, egc)) return; - if (is_eol_right(root, cursor)) + if (is_eol_right(root, cursor, plane)) return; - move_cursor_right(root, cursor) catch return error.Stop; + move_cursor_right(root, cursor, plane) catch return error.Stop; } } @@ -2200,7 +2208,7 @@ pub const Editor = struct { self.push_cursor() catch |e| return tp.exit_error(e); const primary = self.get_primary(); const root = self.buf_root() catch |e| return tp.exit_error(e); - move_cursor_up(root, &primary.cursor) catch {}; + move_cursor_up(root, &primary.cursor, self.plane) catch {}; self.clamp(); } @@ -2214,7 +2222,7 @@ pub const Editor = struct { self.push_cursor() catch |e| return tp.exit_error(e); const primary = self.get_primary(); const root = self.buf_root() catch |e| return tp.exit_error(e); - move_cursor_down(root, &primary.cursor) catch {}; + move_cursor_down(root, &primary.cursor, self.plane) catch {}; self.clamp(); } @@ -2230,7 +2238,7 @@ pub const Editor = struct { const root = self.buf_root() catch return; primary.selection = match.to_selection(); match.has_selection = true; - primary.cursor.move_to(root, match.end.row, match.end.col) catch return; + primary.cursor.move_to(root, match.end.row, match.end.col, self.plane) catch return; } self.clamp(); try self.send_editor_jump_destination(); @@ -2245,7 +2253,7 @@ pub const Editor = struct { const root = self.buf_root() catch return; primary.selection = match.to_selection(); match.has_selection = true; - primary.cursor.move_to(root, match.end.row, match.end.col) catch return; + primary.cursor.move_to(root, match.end.row, match.end.col, self.plane) catch return; } self.clamp(); try self.send_editor_jump_destination(); @@ -2264,7 +2272,7 @@ pub const Editor = struct { .col = 0, }, }; - new_cursel.*.?.cursor.move_end(root); + new_cursel.*.?.cursor.move_end(root, self.plane); } } @@ -2281,18 +2289,18 @@ pub const Editor = struct { fn pull_cursel_up(self: *Self, root_: Buffer.Root, cursel: *CurSel, a: Allocator) error{Stop}!Buffer.Root { var root = root_; const saved = cursel.*; - const sel = cursel.expand_selection_to_line(root); + const sel = cursel.expand_selection_to_line(root, self.plane); var sfa = std.heap.stackFallback(4096, self.a); - const cut_text = copy_selection(root, sel.*, sfa.get()) catch return error.Stop; + const cut_text = copy_selection(root, sel.*, sfa.get(), self.plane) catch return error.Stop; defer a.free(cut_text); root = try self.delete_selection(root, cursel, a); - try cursel.cursor.move_up(root); + try cursel.cursor.move_up(root, self.plane); root = self.insert(root, cursel, cut_text, a) catch return error.Stop; cursel.* = saved; - try cursel.cursor.move_up(root); + try cursel.cursor.move_up(root, self.plane); if (cursel.selection) |*sel_| { - try sel_.begin.move_up(root); - try sel_.end.move_up(root); + try sel_.begin.move_up(root, self.plane); + try sel_.end.move_up(root, self.plane); } return root; } @@ -2307,18 +2315,18 @@ pub const Editor = struct { fn pull_cursel_down(self: *Self, root_: Buffer.Root, cursel: *CurSel, a: Allocator) error{Stop}!Buffer.Root { var root = root_; const saved = cursel.*; - const sel = cursel.expand_selection_to_line(root); + const sel = cursel.expand_selection_to_line(root, self.plane); var sfa = std.heap.stackFallback(4096, self.a); - const cut_text = copy_selection(root, sel.*, sfa.get()) catch return error.Stop; + const cut_text = copy_selection(root, sel.*, sfa.get(), self.plane) catch return error.Stop; defer a.free(cut_text); root = try self.delete_selection(root, cursel, a); - try cursel.cursor.move_down(root); + try cursel.cursor.move_down(root, self.plane); root = self.insert(root, cursel, cut_text, a) catch return error.Stop; cursel.* = saved; - try cursel.cursor.move_down(root); + try cursel.cursor.move_down(root, self.plane); if (cursel.selection) |*sel_| { - try sel_.begin.move_down(root); - try sel_.end.move_down(root); + try sel_.begin.move_down(root, self.plane); + try sel_.end.move_down(root, self.plane); } return root; } @@ -2332,10 +2340,10 @@ pub const Editor = struct { fn dupe_cursel_up(self: *Self, root_: Buffer.Root, cursel: *CurSel, a: Allocator) error{Stop}!Buffer.Root { var root = root_; - const sel: Selection = if (cursel.selection) |sel_| sel_ else Selection.line_from_cursor(cursel.cursor, root); + const sel: Selection = if (cursel.selection) |sel_| sel_ else Selection.line_from_cursor(cursel.cursor, root, self.plane); cursel.selection = null; var sfa = std.heap.stackFallback(4096, self.a); - const text = copy_selection(root, sel, sfa.get()) catch return error.Stop; + const text = copy_selection(root, sel, sfa.get(), self.plane) catch return error.Stop; defer a.free(text); cursel.cursor = sel.begin; root = self.insert(root, cursel, text, a) catch return error.Stop; @@ -2353,10 +2361,10 @@ pub const Editor = struct { fn dupe_cursel_down(self: *Self, root_: Buffer.Root, cursel: *CurSel, a: Allocator) error{Stop}!Buffer.Root { var root = root_; - const sel: Selection = if (cursel.selection) |sel_| sel_ else Selection.line_from_cursor(cursel.cursor, root); + const sel: Selection = if (cursel.selection) |sel_| sel_ else Selection.line_from_cursor(cursel.cursor, root, self.plane); cursel.selection = null; var sfa = std.heap.stackFallback(4096, self.a); - const text = copy_selection(root, sel, sfa.get()) catch return error.Stop; + const text = copy_selection(root, sel, sfa.get(), self.plane) catch return error.Stop; defer a.free(text); cursel.cursor = sel.end; root = self.insert(root, cursel, text, a) catch return error.Stop; @@ -2374,10 +2382,10 @@ pub const Editor = struct { fn toggle_cursel_prefix(self: *Self, root_: Buffer.Root, cursel: *CurSel, a: Allocator) error{Stop}!Buffer.Root { var root = root_; const saved = cursel.*; - const sel = cursel.expand_selection_to_line(root); + const sel = cursel.expand_selection_to_line(root, self.plane); var sfa = std.heap.stackFallback(4096, self.a); const alloc = sfa.get(); - const text = copy_selection(root, sel.*, alloc) catch return error.Stop; + const text = copy_selection(root, sel.*, alloc, self.plane) catch return error.Stop; defer a.free(text); root = try self.delete_selection(root, cursel, a); const new_text = text_manip.toggle_prefix_in_text(self.prefix, text, alloc) catch return error.Stop; @@ -2406,8 +2414,8 @@ pub const Editor = struct { const space = " "; var cursel: CurSel = .{}; cursel.cursor = cursor; - const cols = 4 - find_first_non_ws(root, cursel.cursor.row) % 4; - try smart_move_cursor_begin(root, &cursel.cursor); + const cols = 4 - find_first_non_ws(root, cursel.cursor.row, self.plane) % 4; + try smart_move_cursor_begin(root, &cursel.cursor, self.plane); return self.insert(root, &cursel, space[0..cols], a) catch return error.Stop; } @@ -2436,18 +2444,18 @@ pub const Editor = struct { var newroot = root; defer { cursel.* = saved; - cursel.cursor.clamp_to_buffer(newroot); + cursel.cursor.clamp_to_buffer(newroot, self.plane); } cursel.selection = null; cursel.cursor = cursor; - const first = find_first_non_ws(root, cursel.cursor.row); + const first = find_first_non_ws(root, cursel.cursor.row, self.plane); if (first == 0) return error.Stop; const off = first % 4; const cols = if (off == 0) 4 else off; const sel = cursel.enable_selection(); sel.begin.move_begin(); - try sel.end.move_to(root, sel.end.row, cols); - if (cursel.cursor.col < cols) try cursel.cursor.move_to(root, cursel.cursor.row, cols); + try sel.end.move_to(root, sel.end.row, cols, self.plane); + if (cursel.cursor.col < cols) try cursel.cursor.move_to(root, cursel.cursor.row, cols, self.plane); newroot = try self.delete_selection(root, cursel, a); return newroot; } @@ -2561,7 +2569,7 @@ pub const Editor = struct { try self.send_editor_jump_source(); self.cancel_all_selections(); const root = self.buf_root() catch return; - self.get_primary().cursor.move_buffer_end(root); + self.get_primary().cursor.move_buffer_end(root, self.plane); self.clamp(); try self.send_editor_jump_destination(); } @@ -2701,7 +2709,7 @@ pub const Editor = struct { const primary = self.get_primary(); const sel = primary.enable_selection(); const root = self.buf_root() catch |e| return tp.exit_error(e); - expand_selection_to_all(root, sel) catch |e| tp.exit_error(e); + expand_selection_to_all(root, sel, self.plane) catch |e| tp.exit_error(e); primary.cursor = sel.end; self.clamp(); try self.send_editor_jump_destination(); @@ -2712,8 +2720,8 @@ pub const Editor = struct { const sel = cursel.enable_selection(); defer cursel.check_selection(); sel.normalize(); - try move_cursor_word_begin(root, &sel.begin); - try move_cursor_word_end(root, &sel.end); + try move_cursor_word_begin(root, &sel.begin, self.plane); + try move_cursor_word_end(root, &sel.end, self.plane); cursel.cursor = sel.end; return sel; } @@ -2722,8 +2730,8 @@ pub const Editor = struct { const root = self.buf_root() catch |e| return tp.exit_error(e); const sel = cursel.enable_selection(); sel.normalize(); - try move_cursor_begin(root, &sel.begin); - try move_cursor_end(root, &sel.end); + try move_cursor_begin(root, &sel.begin, self.plane); + try move_cursor_end(root, &sel.end, self.plane); cursel.cursor = sel.end; } @@ -2767,7 +2775,7 @@ pub const Editor = struct { const b = self.buf_for_update() catch |e| return tp.exit_error(e); var root = b.root; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { - var leading_ws = @min(find_first_non_ws(root, cursel.cursor.row), cursel.cursor.col); + var leading_ws = @min(find_first_non_ws(root, cursel.cursor.row, self.plane), cursel.cursor.col); var sfa = std.heap.stackFallback(512, self.a); const a = sfa.get(); var stream = std.ArrayList(u8).init(a); @@ -2786,9 +2794,9 @@ pub const Editor = struct { const b = self.buf_for_update() catch |e| return tp.exit_error(e); var root = b.root; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { - move_cursor_begin(root, &cursel.cursor) catch |e| return tp.exit_error(e); + move_cursor_begin(root, &cursel.cursor, self.plane) catch |e| return tp.exit_error(e); root = self.insert(root, cursel, "\n", b.a) catch |e| return tp.exit_error(e); - move_cursor_left(root, &cursel.cursor) catch |e| return tp.exit_error(e); + move_cursor_left(root, &cursel.cursor, self.plane) catch |e| return tp.exit_error(e); }; self.update_buf(root) catch |e| return tp.exit_error(e); self.clamp(); @@ -2798,10 +2806,10 @@ pub const Editor = struct { const b = self.buf_for_update() catch |e| return tp.exit_error(e); var root = b.root; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { - var leading_ws = @min(find_first_non_ws(root, cursel.cursor.row), cursel.cursor.col); - move_cursor_begin(root, &cursel.cursor) catch |e| return tp.exit_error(e); + var leading_ws = @min(find_first_non_ws(root, cursel.cursor.row, self.plane), cursel.cursor.col); + move_cursor_begin(root, &cursel.cursor, self.plane) catch |e| return tp.exit_error(e); root = self.insert(root, cursel, "\n", b.a) catch |e| return tp.exit_error(e); - move_cursor_left(root, &cursel.cursor) catch |e| return tp.exit_error(e); + move_cursor_left(root, &cursel.cursor, self.plane) catch |e| return tp.exit_error(e); var sfa = std.heap.stackFallback(512, self.a); const a = sfa.get(); var stream = std.ArrayList(u8).init(a); @@ -2820,7 +2828,7 @@ pub const Editor = struct { const b = self.buf_for_update() catch |e| return tp.exit_error(e); var root = b.root; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { - move_cursor_end(root, &cursel.cursor) catch |e| return tp.exit_error(e); + move_cursor_end(root, &cursel.cursor, self.plane) catch |e| return tp.exit_error(e); root = self.insert(root, cursel, "\n", b.a) catch |e| return tp.exit_error(e); }; self.update_buf(root) catch |e| return tp.exit_error(e); @@ -2831,8 +2839,8 @@ pub const Editor = struct { const b = self.buf_for_update() catch |e| return tp.exit_error(e); var root = b.root; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { - var leading_ws = @min(find_first_non_ws(root, cursel.cursor.row), cursel.cursor.col); - move_cursor_end(root, &cursel.cursor) catch |e| return tp.exit_error(e); + var leading_ws = @min(find_first_non_ws(root, cursel.cursor.row, self.plane), cursel.cursor.col); + move_cursor_end(root, &cursel.cursor, self.plane) catch |e| return tp.exit_error(e); var sfa = std.heap.stackFallback(512, self.a); const a = sfa.get(); var stream = std.ArrayList(u8).init(a); @@ -2900,7 +2908,7 @@ pub const Editor = struct { const primary = self.get_primary(); var tree = std.ArrayList(u8).init(self.a); defer tree.deinit(); - root.debug_render_chunks(primary.cursor.row, &tree) catch |e| + root.debug_render_chunks(primary.cursor.row, &tree, self.plane) catch |e| return self.logger.print("line {d}: {any}", .{ primary.cursor.row, e }); self.logger.print("line {d}:{s}", .{ primary.cursor.row, std.fmt.fmtSliceEscapeLower(tree.items) }); } @@ -3156,8 +3164,8 @@ pub const Editor = struct { const root = self.buf_root() catch return; const begin_line = begin_line_ - 1; const end_line = end_line_ - 1; - const begin_pos = root.pos_to_width(begin_line, begin_pos_) catch return; - const end_pos = root.pos_to_width(end_line, end_pos_) catch return; + const begin_pos = root.pos_to_width(begin_line, begin_pos_, self.plane) catch return; + const end_pos = root.pos_to_width(end_line, end_pos_, self.plane) catch return; var match: Match = .{ .begin = .{ .row = begin_line, .col = begin_pos }, .end = .{ .row = end_line, .col = end_pos } }; if (match.end.eql(self.get_primary().cursor)) match.has_selection = true; @@ -3205,7 +3213,7 @@ pub const Editor = struct { if (self.scan_prev_match(cursor)) |match| return match; const root = self.buf_root() catch return null; var cursor_ = cursor; - cursor_.move_buffer_end(root); + cursor_.move_buffer_end(root, self.plane); return self.scan_prev_match(cursor_); } @@ -3214,7 +3222,7 @@ pub const Editor = struct { if (self.get_next_match(primary.cursor)) |match| { const root = self.buf_root() catch return; primary.selection = match.to_selection(); - primary.cursor.move_to(root, match.end.row, match.end.col) catch return; + primary.cursor.move_to(root, match.end.row, match.end.col, self.plane) catch return; self.clamp(); } } @@ -3238,7 +3246,7 @@ pub const Editor = struct { const root = self.buf_root() catch return; primary.selection = match.to_selection(); primary.selection.?.reverse(); - primary.cursor.move_to(root, match.begin.row, match.begin.col) catch return; + primary.cursor.move_to(root, match.begin.row, match.begin.col, self.plane) catch return; self.clamp(); } } @@ -3285,7 +3293,7 @@ pub const Editor = struct { const primary = self.get_primary(); try self.send_editor_jump_source(); self.cancel_all_selections(); - primary.cursor.move_to(root, diag.sel.begin.row, diag.sel.begin.col) catch |e| return tp.exit_error(e); + primary.cursor.move_to(root, diag.sel.begin.row, diag.sel.begin.col, self.plane) catch |e| return tp.exit_error(e); self.clamp(); try self.send_editor_jump_destination(); } @@ -3310,7 +3318,7 @@ pub const Editor = struct { const root = self.buf_root() catch return; self.cancel_all_selections(); const primary = self.get_primary(); - primary.cursor.move_to(root, @intCast(if (line < 1) 0 else line - 1), primary.cursor.col) catch |e| return tp.exit_error(e); + primary.cursor.move_to(root, @intCast(if (line < 1) 0 else line - 1), primary.cursor.col, self.plane) catch |e| return tp.exit_error(e); self.clamp(); try self.send_editor_jump_destination(); } @@ -3321,7 +3329,7 @@ pub const Editor = struct { return tp.exit_error(error.InvalidArgument); const root = self.buf_root() catch return; const primary = self.get_primary(); - primary.cursor.move_to(root, primary.cursor.row, @intCast(if (column < 1) 0 else column - 1)) catch |e| return tp.exit_error(e); + primary.cursor.move_to(root, primary.cursor.row, @intCast(if (column < 1) 0 else column - 1), self.plane) catch |e| return tp.exit_error(e); self.clamp(); } @@ -3354,6 +3362,7 @@ pub const Editor = struct { root, @intCast(if (line < 1) 0 else line - 1), @intCast(if (column < 1) 0 else column - 1), + self.plane, ) catch |e| return tp.exit_error(e); if (have_sel) primary.selection = sel; if (self.view.is_visible(&primary.cursor)) @@ -3442,7 +3451,7 @@ pub const Editor = struct { const primary = self.get_primary(); var sel: Selection = if (primary.selection) |sel_| sel_ else val: { var sel_: Selection = .{}; - expand_selection_to_all(root, &sel_) catch |e| tp.exit_error(e); + expand_selection_to_all(root, &sel_, self.plane) catch |e| tp.exit_error(e); break :val sel_; }; const reversed = sel.begin.right_of(sel.end); @@ -3465,10 +3474,10 @@ pub const Editor = struct { sp.deinit(); } var buffer = sp.bufferedWriter(); - try self.write_range(state.before_root, sel, buffer.writer(), tp.exit_error, null); + try self.write_range(state.before_root, sel, buffer.writer(), tp.exit_error, null, self.plane); try buffer.flush(); self.logger.print("filter: sent", .{}); - state.work_root = state.work_root.delete_range(sel, buf_a_, null) catch |e| return tp.exit_error(e); + state.work_root = state.work_root.delete_range(sel, buf_a_, null, self.plane) catch |e| return tp.exit_error(e); } fn filter_stdout(self: *Self, bytes: []const u8) tp.result { @@ -3479,7 +3488,7 @@ pub const Editor = struct { buf.appendSlice(bytes) catch |e| return tp.exit_error(e); } else { const cursor = &state.pos.cursor; - cursor.row, cursor.col, state.work_root = state.work_root.insert_chars(cursor.row, cursor.col, bytes, buf_a_) catch |e| return tp.exit_error(e); + cursor.row, cursor.col, state.work_root = state.work_root.insert_chars(cursor.row, cursor.col, bytes, buf_a_, self.plane) catch |e| return tp.exit_error(e); state.bytes += bytes.len; state.chunks += 1; } @@ -3512,7 +3521,7 @@ pub const Editor = struct { primary.cursor = sel.end; } self.update_buf(state.work_root) catch |e| return tp.exit_error(e); - primary.cursor.clamp_to_buffer(state.work_root); + primary.cursor.clamp_to_buffer(state.work_root, self.plane); self.logger.print("filter: done (bytes:{d} chunks:{d})", .{ state.bytes, state.chunks }); self.reset_syntax(); self.clamp(); @@ -3652,8 +3661,7 @@ pub const EditorWidget = struct { } fn mouse_click_button1(self: *Self, y: c_int, x: c_int, _: c_int, _: c_int) tp.result { - var y_, var x_ = .{ y, x }; - self.editor.plane.abs_yx_to_rel(&y_, &x_); + const y_, const x_ = self.editor.plane.abs_yx_to_rel(y, x); if (self.last_btn == key.BUTTON1) { const click_time_ms = time.milliTimestamp() - self.last_btn_time_ms; if (click_time_ms <= double_click_time_ms) { @@ -3673,8 +3681,7 @@ pub const EditorWidget = struct { } fn mouse_drag_button1(self: *Self, y: c_int, x: c_int, _: c_int, _: c_int) tp.result { - var y_, var x_ = .{ y, x }; - self.editor.plane.abs_yx_to_rel(&y_, &x_); + const y_, const x_ = self.editor.plane.abs_yx_to_rel(y, x); try self.editor.primary_drag(y_, x_); } @@ -3683,14 +3690,12 @@ pub const EditorWidget = struct { fn mouse_drag_button2(_: *Self, _: c_int, _: c_int, _: c_int, _: c_int) tp.result {} fn mouse_click_button3(self: *Self, y: c_int, x: c_int, _: c_int, _: c_int) tp.result { - var y_, var x_ = .{ y, x }; - self.editor.plane.abs_yx_to_rel(&y_, &x_); + const y_, const x_ = self.editor.plane.abs_yx_to_rel(y, x); try self.editor.secondary_click(y_, x_); } fn mouse_drag_button3(self: *Self, y: c_int, x: c_int, _: c_int, _: c_int) tp.result { - var y_, var x_ = .{ y, x }; - self.editor.plane.abs_yx_to_rel(&y_, &x_); + const y_, const x_ = self.editor.plane.abs_yx_to_rel(y, x); try self.editor.secondary_drag(y_, x_); } @@ -3742,17 +3747,17 @@ pub const PosToWidthCache = struct { self.cache.deinit(); } - pub fn range_to_selection(self: *Self, range: syntax.Range, root: Buffer.Root) ?Selection { + pub fn range_to_selection(self: *Self, range: syntax.Range, root: Buffer.Root, plane: Plane) ?Selection { const start = range.start_point; const end = range.end_point; if (root != self.cached_root or self.cached_line != start.row) { self.cache.clearRetainingCapacity(); self.cached_line = start.row; self.cached_root = root; - root.get_line_width_map(self.cached_line, &self.cache) catch return null; + root.get_line_width_map(self.cached_line, &self.cache, plane) catch return null; } const start_col = if (start.column < self.cache.items.len) self.cache.items[start.column] else start.column; - const end_col = if (end.row == start.row and end.column < self.cache.items.len) self.cache.items[end.column] else root.pos_to_width(end.row, end.column) catch end.column; + const end_col = if (end.row == start.row and end.column < self.cache.items.len) self.cache.items[end.column] else root.pos_to_width(end.row, end.column, plane) catch end.column; return .{ .begin = .{ .row = start.row, .col = start_col }, .end = .{ .row = end.row, .col = end_col } }; } }; diff --git a/src/tui/fonts.zig b/src/tui/fonts.zig index 4af8576..328c697 100644 --- a/src/tui/fonts.zig +++ b/src/tui/fonts.zig @@ -1,17 +1,18 @@ const Plane = @import("renderer").Plane; +const Style = @import("theme").Style; -pub fn print_string_large(n: Plane, s: []const u8) !void { +pub fn print_string_large(n: *Plane, s: []const u8, style: Style) !void { for (s) |c| - print_char_large(n, c) catch break; + print_char_large(n, c, style) catch break; } -pub fn print_char_large(n: Plane, char: u8) !void { +pub fn print_char_large(n: *Plane, char: u8, style: Style) !void { const bitmap = font8x8[char]; for (0..8) |y| { for (0..8) |x| { const set = bitmap[y] & @as(usize, 1) << @intCast(x); if (set != 0) { - _ = try n.putstr("█"); + try write_cell(n, "█", style); } else { n.cursor_move_rel(0, 1) catch {}; } @@ -21,14 +22,14 @@ pub fn print_char_large(n: Plane, char: u8) !void { n.cursor_move_rel(-8, 8) catch {}; } -pub fn print_string_medium(n: Plane, s: []const u8) !void { +pub fn print_string_medium(n: *Plane, s: []const u8, style: Style) !void { for (s) |c| - print_char_medium(n, c) catch break; + print_char_medium(n, c, style) catch break; } const QUADBLOCKS = [_][:0]const u8{ " ", "▘", "▝", "▀", "▖", "▌", "▞", "▛", "▗", "▚", "▐", "▜", "▄", "▙", "▟", "█" }; -pub fn print_char_medium(n: Plane, char: u8) !void { +pub fn print_char_medium(n: *Plane, char: u8, style: Style) !void { const bitmap = font8x8[char]; for (0..4) |y| { for (0..4) |x| { @@ -43,7 +44,7 @@ pub fn print_char_medium(n: Plane, char: u8) !void { const quadidx: u4 = setbr | setbl | settr | settl; const c = QUADBLOCKS[quadidx]; if (quadidx != 0) { - _ = try n.putstr(c); + try write_cell(n, c, style); } else { n.cursor_move_rel(0, 1) catch {}; } @@ -53,6 +54,14 @@ pub fn print_char_medium(n: Plane, char: u8) !void { n.cursor_move_rel(-4, 4) catch {}; } +fn write_cell(n: *Plane, egc: [:0]const u8, style: Style) !void { + var cell = n.cell_init(); + _ = n.at_cursor_cell(&cell) catch return; + _ = n.cell_load(&cell, egc) catch {}; + cell.set_style_fg(style); + _ = n.putc(&cell) catch {}; +} + pub const font8x8: [128][8]u8 = [128][8]u8{ [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, [8]u8{ 0, 0, 0, 0, 0, 0, 0, 0 }, diff --git a/src/tui/home.zig b/src/tui/home.zig index 1278b96..c36f40b 100644 --- a/src/tui/home.zig +++ b/src/tui/home.zig @@ -15,7 +15,6 @@ const command = @import("command.zig"); const fonts = @import("fonts.zig"); a: std.mem.Allocator, -background: Plane, plane: Plane, parent: Plane, fire: ?Fire = null, @@ -26,8 +25,6 @@ const Self = @This(); pub fn create(a: std.mem.Allocator, parent: Widget) !Widget { const self: *Self = try a.create(Self); - var background = try Plane.init(&(Widget.Box{}).opts("background"), parent.plane.*); - errdefer background.deinit(); var n = try Plane.init(&(Widget.Box{}).opts("editor"), parent.plane.*); errdefer n.deinit(); @@ -35,7 +32,6 @@ pub fn create(a: std.mem.Allocator, parent: Widget) !Widget { self.* = .{ .a = a, .parent = parent.plane.*, - .background = background, .plane = n, .menu = try Menu.create(*Self, a, w, .{ .ctx = self, .on_render = menu_on_render }), }; @@ -56,7 +52,6 @@ pub fn deinit(self: *Self, a: std.mem.Allocator) void { self.menu.deinit(a); self.commands.deinit(); self.plane.deinit(); - self.background.deinit(); if (self.fire) |*fire| fire.deinit(); a.destroy(self); } @@ -130,16 +125,13 @@ fn menu_action_quit(_: **Menu.State(*Self), _: *Button.State(*Menu.State(*Self)) } pub fn render(self: *Self, theme: *const Widget.Theme) bool { - const more = self.menu.render(theme); - - self.background.set_base_style(" ", theme.editor); - self.background.erase(); - self.background.home(); self.plane.set_base_style_transparent("", theme.editor); self.plane.erase(); self.plane.home(); if (self.fire) |*fire| fire.render(); + const more = self.menu.render(theme); + const style_title = if (tui.find_scope_style(theme, "function")) |sty| sty.style else theme.editor; const style_subtext = if (tui.find_scope_style(theme, "comment")) |sty| sty.style else theme.editor; @@ -147,19 +139,16 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { const subtext = "a programmer's text editor"; if (self.plane.dim_x() > 120 and self.plane.dim_y() > 22) { - self.plane.set_style_bg_transparent(style_title); self.plane.cursor_move_yx(2, 4) catch return more; - fonts.print_string_large(self.plane, title) catch return more; + fonts.print_string_large(&self.plane, title, style_title) catch return more; - self.plane.set_style_bg_transparent(style_subtext); self.plane.cursor_move_yx(10, 8) catch return more; - fonts.print_string_medium(self.plane, subtext) catch return more; + fonts.print_string_medium(&self.plane, subtext, style_subtext) catch return more; self.menu.resize(.{ .y = 15, .x = 10, .w = 32 }); } else if (self.plane.dim_x() > 55 and self.plane.dim_y() > 16) { - self.plane.set_style_bg_transparent(style_title); self.plane.cursor_move_yx(2, 4) catch return more; - fonts.print_string_medium(self.plane, title) catch return more; + fonts.print_string_medium(&self.plane, title, style_title) catch return more; self.plane.set_style_bg_transparent(style_subtext); self.plane.cursor_move_yx(7, 6) catch return more; @@ -181,13 +170,11 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { } pub fn handle_resize(self: *Self, pos: Widget.Box) void { - self.background.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return; - self.background.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return; self.plane.move_yx(@intCast(pos.y), @intCast(pos.x)) catch return; self.plane.resize_simple(@intCast(pos.h), @intCast(pos.w)) catch return; if (self.fire) |*fire| { fire.deinit(); - self.fire = Fire.init(self.a, self.plane, pos) catch return; + self.fire = Fire.init(self.a, self.plane) catch return; } } @@ -213,7 +200,7 @@ const cmds = struct { self.fire = if (self.fire) |*fire| ret: { fire.deinit(); break :ret null; - } else Fire.init(self.a, self.background, Widget.Box.from(self.background)) catch |e| return tp.exit_error(e); + } else Fire.init(self.a, self.plane) catch |e| return tp.exit_error(e); } }; @@ -239,7 +226,8 @@ const Fire = struct { const MAX_COLOR = 256; const LAST_COLOR = MAX_COLOR - 1; - fn init(a: std.mem.Allocator, plane: Plane, pos: Widget.Box) !Fire { + fn init(a: std.mem.Allocator, plane: Plane) !Fire { + const pos = Widget.Box.from(plane); const FIRE_H = @as(u16, @intCast(pos.h)) * 2; const FIRE_W = @as(u16, @intCast(pos.w)); var self: Fire = .{ diff --git a/src/tui/inspector_view.zig b/src/tui/inspector_view.zig index 2da7cd6..fc661ac 100644 --- a/src/tui/inspector_view.zig +++ b/src/tui/inspector_view.zig @@ -99,11 +99,11 @@ fn inspect_location(self: *Self, row: usize, col: usize) void { fn get_buffer_text(self: *Self, buf: []u8, sel: Buffer.Selection) ?[]const u8 { const root = self.editor.get_current_root() orelse return null; - return root.get_range(sel, buf, null, null) catch return null; + return root.get_range(sel, buf, null, null, self.plane) catch return null; } fn dump_highlight(self: *Self, range: syntax.Range, scope: []const u8, id: u32, _: usize, ast_node: *const syntax.Node) error{Stop}!void { - const sel = self.pos_cache.range_to_selection(range, self.editor.get_current_root() orelse return) orelse return; + const sel = self.pos_cache.range_to_selection(range, self.editor.get_current_root() orelse return, self.plane) orelse return; if (self.need_clear) { self.need_clear = false; self.clear(); @@ -138,7 +138,7 @@ fn dump_highlight(self: *Self, range: syntax.Range, scope: []const u8, id: u32, const ast_parent = parent.asSExpressionString(); _ = self.plane.print("parent: {s}\n", .{ast_parent}) catch {}; syntax.Node.freeSExpressionString(ast_parent); - const sel_parent = self.pos_cache.range_to_selection(parent.getRange(), self.editor.get_current_root() orelse return) orelse return; + const sel_parent = self.pos_cache.range_to_selection(parent.getRange(), self.editor.get_current_root() orelse return, self.plane) orelse return; var match_parent = ed.Match.from_selection(sel_parent); if (self.theme) |theme| match_parent.style = .{ .bg = theme.editor_gutter_added.fg }; switch (update_match) { diff --git a/src/tui/mode/input/flow.zig b/src/tui/mode/input/flow.zig index 3737f02..44052a2 100644 --- a/src/tui/mode/input/flow.zig +++ b/src/tui/mode/input/flow.zig @@ -4,7 +4,7 @@ const root = @import("root"); const key = @import("renderer").input.key; const mod = @import("renderer").input.modifier; const event_type = @import("renderer").input.event_type; -const egc_ = @import("renderer").egc; +const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8; const tui = @import("../../tui.zig"); const command = @import("../../command.zig"); @@ -250,7 +250,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result { if (self.input.items.len + 4 > input_buffer_size) try self.flush_input(); var buf: [6]u8 = undefined; - const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); + const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e); } diff --git a/src/tui/mode/input/vim/insert.zig b/src/tui/mode/input/vim/insert.zig index 4478b0d..47a00e9 100644 --- a/src/tui/mode/input/vim/insert.zig +++ b/src/tui/mode/input/vim/insert.zig @@ -4,7 +4,7 @@ const root = @import("root"); const key = @import("renderer").input.key; const mod = @import("renderer").input.modifier; const event_type = @import("renderer").input.event_type; -const egc_ = @import("renderer").egc; +const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8; const tui = @import("../../../tui.zig"); const command = @import("../../../command.zig"); @@ -251,7 +251,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result { if (self.input.items.len + 4 > input_buffer_size) try self.flush_input(); var buf: [6]u8 = undefined; - const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); + const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e); } diff --git a/src/tui/mode/input/vim/normal.zig b/src/tui/mode/input/vim/normal.zig index 6ee8d9b..e7487e1 100644 --- a/src/tui/mode/input/vim/normal.zig +++ b/src/tui/mode/input/vim/normal.zig @@ -4,7 +4,7 @@ const root = @import("root"); const key = @import("renderer").input.key; const mod = @import("renderer").input.modifier; const event_type = @import("renderer").input.event_type; -const egc_ = @import("renderer").egc; +const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8; const tui = @import("../../../tui.zig"); const command = @import("../../../command.zig"); @@ -453,7 +453,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result { if (self.input.items.len + 4 > input_buffer_size) try self.flush_input(); var buf: [6]u8 = undefined; - const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); + const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e); } diff --git a/src/tui/mode/input/vim/visual.zig b/src/tui/mode/input/vim/visual.zig index df6c945..7cb81ba 100644 --- a/src/tui/mode/input/vim/visual.zig +++ b/src/tui/mode/input/vim/visual.zig @@ -4,7 +4,7 @@ const root = @import("root"); const key = @import("renderer").input.key; const mod = @import("renderer").input.modifier; const event_type = @import("renderer").input.event_type; -const egc_ = @import("renderer").egc; +const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8; const tui = @import("../../../tui.zig"); const command = @import("../../../command.zig"); @@ -411,7 +411,7 @@ fn insert_code_point(self: *Self, c: u32) tp.result { if (self.input.items.len + 4 > input_buffer_size) try self.flush_input(); var buf: [6]u8 = undefined; - const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); + const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); self.input.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e); } diff --git a/src/tui/mode/mini/find.zig b/src/tui/mode/mini/find.zig index 7531ec1..3af41f9 100644 --- a/src/tui/mode/mini/find.zig +++ b/src/tui/mode/mini/find.zig @@ -3,7 +3,7 @@ const tp = @import("thespian"); const key = @import("renderer").input.key; const mod = @import("renderer").input.modifier; const event_type = @import("renderer").input.event_type; -const egc_ = @import("renderer").egc; +const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8; const tui = @import("../../tui.zig"); const mainview = @import("../../mainview.zig"); @@ -161,7 +161,7 @@ fn mapRelease(self: *Self, keypress: u32, _: u32, _: u32) tp.result { fn insert_code_point(self: *Self, c: u32) tp.result { if (self.input.len + 16 > self.buf.len) try self.flush_input(); - const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e); + const bytes = ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e); self.input = self.buf[0 .. self.input.len + bytes]; } diff --git a/src/tui/mode/mini/find_in_files.zig b/src/tui/mode/mini/find_in_files.zig index 1b9b00f..5b64df2 100644 --- a/src/tui/mode/mini/find_in_files.zig +++ b/src/tui/mode/mini/find_in_files.zig @@ -3,7 +3,7 @@ const tp = @import("thespian"); const key = @import("renderer").input.key; const mod = @import("renderer").input.modifier; const event_type = @import("renderer").input.event_type; -const egc_ = @import("renderer").egc; +const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8; const tui = @import("../../tui.zig"); const mainview = @import("../../mainview.zig"); @@ -158,7 +158,7 @@ fn mapRelease(self: *Self, keypress: u32, _: u32, _: u32) tp.result { fn insert_code_point(self: *Self, c: u32) tp.result { if (self.input.len + 16 > self.buf.len) try self.flush_input(); - const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e); + const bytes = ucs32_to_utf8(&[_]u32{c}, self.buf[self.input.len..]) catch |e| return tp.exit_error(e); self.input = self.buf[0 .. self.input.len + bytes]; } diff --git a/src/tui/mode/mini/move_to_char.zig b/src/tui/mode/mini/move_to_char.zig index b46ef91..1c7aa0f 100644 --- a/src/tui/mode/mini/move_to_char.zig +++ b/src/tui/mode/mini/move_to_char.zig @@ -3,7 +3,7 @@ const tp = @import("thespian"); const key = @import("renderer").input.key; const mod = @import("renderer").input.modifier; const event_type = @import("renderer").input.event_type; -const egc_ = @import("renderer").egc; +const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8; const tui = @import("../../tui.zig"); const mainview = @import("../../mainview.zig"); @@ -114,7 +114,7 @@ fn execute_operation(self: *Self, c: u32) void { }, }; var buf: [6]u8 = undefined; - const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch return; + const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch return; command.executeName(cmd, command.fmt(.{buf[0..bytes]})) catch {}; command.executeName("exit_mini_mode", .{}) catch {}; } diff --git a/src/tui/mode/mini/open_file.zig b/src/tui/mode/mini/open_file.zig index 9d8558c..74086a8 100644 --- a/src/tui/mode/mini/open_file.zig +++ b/src/tui/mode/mini/open_file.zig @@ -4,7 +4,7 @@ const tp = @import("thespian"); const key = @import("renderer").input.key; const mod = @import("renderer").input.modifier; const event_type = @import("renderer").input.event_type; -const egc_ = @import("renderer").egc; +const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8; const tui = @import("../../tui.zig"); const mainview = @import("../../mainview.zig"); @@ -125,7 +125,7 @@ fn mapRelease(_: *Self, _: u32, _: u32, _: u32) tp.result {} fn insert_code_point(self: *Self, c: u32) tp.result { var buf: [32]u8 = undefined; - const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); + const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); self.file_path.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e); } diff --git a/src/tui/mode/overlay/open_recent.zig b/src/tui/mode/overlay/open_recent.zig index 819e7f2..d91f4d5 100644 --- a/src/tui/mode/overlay/open_recent.zig +++ b/src/tui/mode/overlay/open_recent.zig @@ -8,7 +8,7 @@ const planeutils = @import("renderer").planeutils; const key = @import("renderer").input.key; const mod = @import("renderer").input.modifier; const event_type = @import("renderer").input.event_type; -const egc_ = @import("renderer").egc; +const ucs32_to_utf8 = @import("renderer").ucs32_to_utf8; const tui = @import("../../tui.zig"); const command = @import("../../command.zig"); @@ -89,13 +89,13 @@ fn on_render_menu(_: *Self, button: *Button.State(*Menu.State(*Self)), theme: *c var len = cbor.decodeArrayHeader(&iter) catch return false; while (len > 0) : (len -= 1) { if (cbor.matchValue(&iter, cbor.extract(&index)) catch break) { - render_cell(button.plane, 0, index + 1, theme.editor_match) catch break; + render_cell(&button.plane, 0, index + 1, theme.editor_match) catch break; } else break; } return false; } -fn render_cell(plane: Plane, y: usize, x: usize, style: Widget.Theme.Style) !void { +fn render_cell(plane: *Plane, y: usize, x: usize, style: Widget.Theme.Style) !void { plane.cursor_move_yx(@intCast(y), @intCast(x)) catch return; var cell = plane.cell_init(); _ = plane.at_cursor_cell(&cell) catch return; @@ -313,7 +313,7 @@ fn delete_code_point(self: *Self) tp.result { fn insert_code_point(self: *Self, c: u32) tp.result { var buf: [6]u8 = undefined; - const bytes = egc_.ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); + const bytes = ucs32_to_utf8(&[_]u32{c}, &buf) catch |e| return tp.exit_error(e); self.inputbox.text.appendSlice(buf[0..bytes]) catch |e| return tp.exit_error(e); self.inputbox.cursor = self.inputbox.text.items.len; return self.start_query(); diff --git a/src/tui/scrollbar_v.zig b/src/tui/scrollbar_v.zig index 60c130e..71207ab 100644 --- a/src/tui/scrollbar_v.zig +++ b/src/tui/scrollbar_v.zig @@ -123,7 +123,7 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool { defer frame.deinit(); self.plane.set_base_style(" ", if (self.active) theme.scrollbar_active else if (self.hover) theme.scrollbar_hover else theme.scrollbar); self.plane.erase(); - smooth_bar_at(self.plane, @intCast(self.pos_scrn), @intCast(self.view_scrn)) catch {}; + smooth_bar_at(&self.plane, @intCast(self.pos_scrn), @intCast(self.view_scrn)) catch {}; return false; } @@ -151,7 +151,7 @@ const eighths_b = [_][]const u8{ "█", "▇", "▆", "▅", "▄", "▃", "▂" const eighths_t = [_][]const u8{ " ", "▔", "🮂", "🮃", "▀", "🮄", "🮅", "🮆" }; const eighths_c: i32 = @intCast(eighths_b.len); -fn smooth_bar_at(plane: Plane, pos_: i32, size_: i32) !void { +fn smooth_bar_at(plane: *Plane, pos_: i32, size_: i32) !void { const height: i32 = @intCast(plane.dim_y()); var size = @max(size_, 8); const pos: i32 = @min(height * eighths_c - size, pos_); diff --git a/src/tui/status/keystate.zig b/src/tui/status/keystate.zig index 1368d95..1f209e2 100644 --- a/src/tui/status/keystate.zig +++ b/src/tui/status/keystate.zig @@ -195,7 +195,7 @@ fn animate(self: *Self) bool { else frame; - smooth_block_at(self.plane, pos); + smooth_block_at(&self.plane, pos); return false; // return pos != 0; } @@ -204,7 +204,7 @@ const eighths_l = [_][]const u8{ "█", "▉", "▊", "▋", "▌", "▍", "▎" const eighths_r = [_][]const u8{ " ", "▕", "🮇", "🮈", "▐", "🮉", "🮊", "🮋" }; const eighths_c = eighths_l.len; -fn smooth_block_at(plane: Plane, pos: u64) void { +fn smooth_block_at(plane: *Plane, pos: u64) void { const blk = @mod(pos, eighths_c) + 1; const l = eighths_l[eighths_c - blk]; const r = eighths_r[eighths_c - blk]; diff --git a/src/tui/status/modestate.zig b/src/tui/status/modestate.zig index a6b6c86..b0e0f83 100644 --- a/src/tui/status/modestate.zig +++ b/src/tui/status/modestate.zig @@ -3,7 +3,6 @@ const Allocator = std.mem.Allocator; const tp = @import("thespian"); const tracy = @import("tracy"); const root = @import("root"); -const egc = @import("renderer").egc; const Plane = @import("renderer").Plane; const style = @import("renderer").style; @@ -25,9 +24,9 @@ pub fn create(a: Allocator, parent: Plane) !Widget { }); } -pub fn layout(_: *void, _: *Button.State(void)) Widget.Layout { +pub fn layout(_: *void, btn: *Button.State(void)) Widget.Layout { const name = tui.get_mode(); - const width = egc.chunk_width(name, 0); + const width = btn.plane.egc_chunk_width(name, 0); const padding: usize = if (is_mini_mode()) 3 else 2; return .{ .static = width + padding }; } diff --git a/src/tui/tui.zig b/src/tui/tui.zig index ac2175a..bf1af0a 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -93,13 +93,6 @@ fn init(a: Allocator) !*Self { const frame_time = std.time.us_per_s / conf.frame_rate; const frame_clock = try tp.metronome.init(frame_time); - const fd_stdin = try tp.file_descriptor.init("stdin", ctx.input_fd()); - // const fd_stdin = try tp.file_descriptor.init("stdin", std.os.STDIN_FILENO); - const n = ctx.stdplane(); - - try frame_clock.start(); - try fd_stdin.wait_read(); - self.* = .{ .a = a, .config = conf, @@ -107,7 +100,7 @@ fn init(a: Allocator) !*Self { .frame_time = frame_time, .frame_clock = frame_clock, .frame_clock_running = true, - .fd_stdin = fd_stdin, + .fd_stdin = undefined, .receiver = Receiver.init(receive, self), .mainview = undefined, .message_filters = MessageFilter.List.init(a), @@ -120,6 +113,15 @@ fn init(a: Allocator) !*Self { }; instance_ = self; defer instance_ = null; + + try self.rdr.run(); + self.fd_stdin = try tp.file_descriptor.init("stdin", self.rdr.input_fd()); + // self.fd_stdin = try tp.file_descriptor.init("stdin", std.os.STDIN_FILENO); + const n = self.rdr.stdplane(); + + try frame_clock.start(); + try self.fd_stdin.wait_read(); + self.rdr.handler_ctx = self; self.rdr.dispatch_input = dispatch_input; self.rdr.dispatch_mouse = dispatch_mouse; @@ -129,7 +131,7 @@ fn init(a: Allocator) !*Self { errdefer self.deinit(); try self.listen_sigwinch(); self.mainview = try mainview.create(a, n); - try ctx.render(); + try self.rdr.render(); try self.save_config(); if (tp.env.get().is("restore-session")) { command.executeName("restore_session", .{}) catch |e| self.logger.err("restore_session", e); @@ -305,7 +307,7 @@ fn render(self: *Self, current_time: i64) void { }; { - const frame = tracy.initZone(@src(), .{ .name = "notcurses render" }); + const frame = tracy.initZone(@src(), .{ .name = renderer.log_name ++ " render" }); defer frame.deinit(); self.rdr.render() catch |e| self.logger.err("render", e); } @@ -342,7 +344,10 @@ fn dispatch_input_fd(self: *Self, m: tp.message) error{Exit}!bool { var err_msg: []u8 = ""; if (try m.match(.{ "fd", "stdin", "read_ready" })) { self.fd_stdin.wait_read() catch |e| return tp.exit_error(e); - self.rdr.process_input() catch |e| return tp.exit_error(e); + self.rdr.process_input() catch |e| switch (e) { + error.WouldBlock => return true, + else => return tp.exit_error(e), + }; try self.dispatch_flush_input_event(); if (self.unrendered_input_events_count > 0 and !self.frame_clock_running) need_render(); @@ -512,18 +517,17 @@ const cmds = struct { const l = log.logger("z stack"); defer l.deinit(); var buf: [256]u8 = undefined; - var buf_parent: [256]u8 = undefined; var z: i32 = 0; var n = self.rdr.stdplane(); while (n.below()) |n_| : (n = n_) { z -= 1; - l.print("{d} {s} {s}", .{ z, n_.name(&buf), n_.parent().name(&buf_parent) }); + l.print("{d} {s}", .{ z, n_.name(&buf) }); } z = 0; n = self.rdr.stdplane(); while (n.above()) |n_| : (n = n_) { z += 1; - l.print("{d} {s} {s}", .{ z, n_.name(&buf), n_.parent().name(&buf_parent) }); + l.print("{d} {s}", .{ z, n_.name(&buf) }); } }