From 46e7fd8fc663f95415c418c2d76e16e80741fd35 Mon Sep 17 00:00:00 2001 From: Tim Culverhouse Date: Tue, 11 Jun 2024 20:35:10 -0500 Subject: [PATCH] widgets: remove neovim widget The neovim widget was great. But we have a full virtual terminal widget now, which doesn't bring in extra dependencies. --- build.zig | 8 - build.zig.zon | 5 - examples/nvim.zig | 83 ----- src/widgets.zig | 1 - src/widgets/nvim.zig | 763 ------------------------------------------- 5 files changed, 860 deletions(-) delete mode 100644 examples/nvim.zig delete mode 100644 src/widgets/nvim.zig diff --git a/build.zig b/build.zig index d7240b4..b51b512 100644 --- a/build.zig +++ b/build.zig @@ -3,13 +3,11 @@ const std = @import("std"); pub fn build(b: *std.Build) void { const include_libxev = b.option(bool, "libxev", "Enable support for libxev library (default: true)") orelse true; const include_images = b.option(bool, "images", "Enable support for images (default: true)") orelse true; - const include_nvim = b.option(bool, "nvim", "Enable support for the neovim widget (default: true)") orelse true; const include_text_input = b.option(bool, "text_input", "Enable support for the TextInput widget (default: true)") orelse true; const options = b.addOptions(); options.addOption(bool, "libxev", include_libxev); options.addOption(bool, "images", include_images); - options.addOption(bool, "nvim", include_nvim); options.addOption(bool, "text_input", include_text_input); const options_mod = options.createModule(); @@ -31,10 +29,6 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .target = target, }) else null; - const znvim_dep = if (include_nvim) b.lazyDependency("znvim", .{ - .optimize = optimize, - .target = target, - }) else null; const xev_dep = if (include_libxev) b.lazyDependency("libxev", .{ .optimize = optimize, .target = target, @@ -51,7 +45,6 @@ pub fn build(b: *std.Build) void { vaxis_mod.addImport("DisplayWidth", zg_dep.module("DisplayWidth")); if (zigimg_dep) |dep| vaxis_mod.addImport("zigimg", dep.module("zigimg")); if (gap_buffer_dep) |dep| vaxis_mod.addImport("gap_buffer", dep.module("gap_buffer")); - if (znvim_dep) |dep| vaxis_mod.addImport("znvim", dep.module("znvim")); if (xev_dep) |dep| vaxis_mod.addImport("xev", dep.module("xev")); vaxis_mod.addImport("build_options", options_mod); @@ -97,7 +90,6 @@ pub fn build(b: *std.Build) void { tests.root_module.addImport("DisplayWidth", zg_dep.module("DisplayWidth")); if (zigimg_dep) |dep| tests.root_module.addImport("zigimg", dep.module("zigimg")); if (gap_buffer_dep) |dep| tests.root_module.addImport("gap_buffer", dep.module("gap_buffer")); - if (znvim_dep) |dep| tests.root_module.addImport("znvim", dep.module("znvim")); tests.root_module.addImport("build_options", options_mod); const tests_run = b.addRunArtifact(tests); diff --git a/build.zig.zon b/build.zig.zon index b0c7059..0bc696f 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -13,11 +13,6 @@ .hash = "1220f525973ae804ec0284556bfc47db7b6a8dc86464a853956ef859d6e0fb5fa93b", .lazy = true, }, - .znvim = .{ - .url = "git+https://github.com/jinzhongjia/znvim#515fb2784f7b0db2ea7a0021aada8d4aa89a08fe", - .hash = "1220ca0a762fdf3172623e3f15890e396c975657d8ec57d907896f74ef94a37b73f2", - .lazy = true, - }, .zg = .{ .url = "git+https://codeberg.org/dude_the_builder/zg?ref=master#689ab6b83d08c02724b99d199d650ff731250998", .hash = "12200d1ce5f9733a9437415d85665ad5fbc85a4d27689fd337fecad8014acffe3aa5", diff --git a/examples/nvim.zig b/examples/nvim.zig deleted file mode 100644 index 1c845a2..0000000 --- a/examples/nvim.zig +++ /dev/null @@ -1,83 +0,0 @@ -const std = @import("std"); -const vaxis = @import("vaxis"); -const Cell = vaxis.Cell; - -// Our Event. This can contain internal events as well as Vaxis events. -// Internal events can be posted into the same queue as vaxis events to allow -// for a single event loop with exhaustive switching. Booya -const Event = union(enum) { - key_press: vaxis.Key, - winsize: vaxis.Winsize, - focus_in, - nvim: vaxis.widgets.nvim.Event, -}; - -pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - defer { - const deinit_status = gpa.deinit(); - //fail test; can't try in defer as defer is executed after we return - if (deinit_status == .leak) { - std.log.err("memory leak", .{}); - } - } - const alloc = gpa.allocator(); - - var tty = try vaxis.Tty.init(); - defer tty.deinit(); - - // Initialize Vaxis - var vx = try vaxis.init(alloc, .{}); - defer vx.deinit(alloc, tty.anyWriter()); - - var loop: vaxis.Loop(Event) = .{ .tty = &tty, .vaxis = &vx }; - try loop.init(); - - try loop.start(); - defer loop.stop(); - - // Optionally enter the alternate screen - // try vx.enterAltScreen(tty.anyWriter()); - try vx.queryTerminal(tty.anyWriter(), 1 * std.time.ns_per_s); - - var nvim = try vaxis.widgets.nvim.Nvim(Event).init(alloc, &loop); - try nvim.spawn(); - defer nvim.deinit(); - - // The main event loop. Vaxis provides a thread safe, blocking, buffered - // queue which can serve as the primary event queue for an application - while (true) { - // nextEvent blocks until an event is in the queue - const event = loop.nextEvent(); - std.log.debug("event: {}", .{event}); - // exhaustive switching ftw. Vaxis will send events if your Event - // enum has the fields for those events (ie "key_press", "winsize") - switch (event) { - .key_press => |key| { - try nvim.update(.{ .key_press = key }); - }, - .winsize => |ws| { - try vx.resize(alloc, tty.anyWriter(), ws); - }, - .nvim => |nvim_event| { - switch (nvim_event) { - .redraw => {}, - .quit => return, - } - }, - else => {}, - } - - const win = vx.window(); - win.clear(); - const child = win.child( - .{ - .height = .{ .limit = 40 }, - .width = .{ .limit = 80 }, - .border = .{ .where = .all }, - }, - ); - try nvim.draw(child); - try vx.render(tty.anyWriter()); - } -} diff --git a/src/widgets.zig b/src/widgets.zig index 14d774e..45084af 100644 --- a/src/widgets.zig +++ b/src/widgets.zig @@ -15,4 +15,3 @@ pub const Terminal = @import("widgets/terminal/Terminal.zig"); // Widgets with dependencies pub const TextInput = if (opts.text_input) @import("widgets/TextInput.zig") else undefined; -pub const nvim = if (opts.nvim) @import("widgets/nvim.zig") else undefined; diff --git a/src/widgets/nvim.zig b/src/widgets/nvim.zig deleted file mode 100644 index e59c7ca..0000000 --- a/src/widgets/nvim.zig +++ /dev/null @@ -1,763 +0,0 @@ -const std = @import("std"); -const assert = std.debug.assert; -const vaxis = @import("../main.zig"); -const znvim = @import("znvim"); - -pub const Event = union(enum) { - redraw: *anyopaque, - quit: *anyopaque, -}; - -pub fn Nvim(comptime T: type) type { - if (!@hasField(T, "nvim")) { - @compileError("Nvim widget requires an Event to have an 'nvim' event of type Nvim.Event"); - } - return struct { - const Self = @This(); - const Client = znvim.DefaultClient(.file); - const EventType = T; - - const log = std.log.scoped(.nvim); - - /// vaxis events handled by Nvim - pub const VaxisEvent = union(enum) { - key_press: vaxis.Key, - }; - - alloc: std.mem.Allocator, - - /// true when we have spawned - spawned: bool = false, - /// true when we have ui attached - attached: bool = false, - - client: ?Client = null, - - /// draw mutex. We lock access to the internal model while drawing - mutex: std.Thread.Mutex = .{}, - - /// the child process - process: std.ChildProcess, - - thread: ?std.Thread = null, - - screen: vaxis.AllocatingScreen = undefined, - visible_screen: vaxis.AllocatingScreen = undefined, - - hl_map: HighlightMap, - - loop: *vaxis.Loop(T), - dirty: bool = false, - mode_set: std.ArrayList(Mode), - - /// initialize nvim. Starts the nvim process. UI is not attached until the first - /// call to draw - pub fn init(alloc: std.mem.Allocator, loop: *vaxis.Loop(T)) !Self { - const args = [_][]const u8{ "nvim", "--embed" }; - var nvim = std.ChildProcess.init(&args, alloc); - - // set to use pipe - nvim.stdin_behavior = .Pipe; - // set to use pipe - nvim.stdout_behavior = .Pipe; - // set ignore - nvim.stderr_behavior = .Ignore; - - // try spwan - try nvim.spawn(); - return .{ - .alloc = alloc, - .process = nvim, - .hl_map = HighlightMap.init(alloc), - .loop = loop, - .mode_set = std.ArrayList(Mode).init(alloc), - }; - } - - /// spawns the client thread and registers callbacks - pub fn spawn(self: *Self) !void { - if (self.spawned) return; - defer self.spawned = true; - - // get stdin and stdout pipe - assert(self.process.stdin != null); - assert(self.process.stdout != null); - const nvim_stdin = self.process.stdin.?; - const nvim_stdout = self.process.stdout.?; - - self.client = try Client.init( - nvim_stdin, - nvim_stdout, - self.alloc, - ); - - self.thread = try std.Thread.spawn(.{}, Self.nvimLoop, .{self}); - } - - pub fn deinit(self: *Self) void { - if (self.client) |*client| { - client.deinit(); - } - _ = self.process.kill() catch |err| - log.err("couldn't kill nvim process: {}", .{err}); - if (self.thread) |thread| { - thread.join(); - } - self.screen.deinit(self.alloc); - self.visible_screen.deinit(self.alloc); - self.hl_map.map.deinit(); - self.mode_set.deinit(); - } - - pub fn draw(self: *Self, win: vaxis.Window) !void { - self.mutex.lock(); - defer self.mutex.unlock(); - self.dirty = false; - win.setCursorShape(self.visible_screen.cursor_shape); - if (!self.attached) try self.attach(win.width, win.height); - if (win.width != self.screen.width or - win.height != self.screen.height) try self.resize(win.width, win.height); - var row: usize = 0; - while (row < self.visible_screen.height) : (row += 1) { - var col: usize = 0; - while (col < self.visible_screen.width) : (col += 1) { - win.writeCell(col, row, self.visible_screen.readCell(col, row).?); - } - } - if (self.visible_screen.cursor_vis) - win.showCursor(self.visible_screen.cursor_col, self.visible_screen.cursor_row); - } - - pub fn update(self: *Self, event: VaxisEvent) !void { - var client = self.client orelse return; - switch (event) { - .key_press => |key| { - const key_str = if (key.text) |text| - text - else blk: { - var buf: [64]u8 = undefined; - var alloc = std.heap.FixedBufferAllocator.init(&buf); - var w = std.ArrayList(u8).init(alloc.allocator()); - try w.append('<'); - if (key.mods.shift) - try w.appendSlice("S-"); - if (key.mods.ctrl) - try w.appendSlice("C-"); - if (key.mods.alt) - try w.appendSlice("M-"); - if (key.mods.super) - try w.appendSlice("D-"); - - const key_str = switch (key.codepoint) { - '<' => "lt", - '\\' => "Bslash", - '|' => "Bar", - vaxis.Key.enter => "CR", - vaxis.Key.backspace => "BS", - vaxis.Key.tab => "Tab", - vaxis.Key.escape => "ESC", - vaxis.Key.space => "Space", - vaxis.Key.delete => "Del", - vaxis.Key.up => "Up", - vaxis.Key.down => "Down", - vaxis.Key.left => "Left", - vaxis.Key.right => "Right", - - else => utf8: { - var utf8Buf: [4]u8 = undefined; - const n = try std.unicode.utf8Encode(key.codepoint, &utf8Buf); - break :utf8 utf8Buf[0..n]; - }, - }; - - try w.appendSlice(key_str); - try w.append('>'); - - break :blk w.items; - }; - var payload = try Client.createParams(1, self.alloc); - defer Client.freeParams(payload, self.alloc); - payload.arr[0] = try znvim.Payload.strToPayload(key_str, self.alloc); - try client.notify("nvim_input", payload); - }, - } - } - - fn attach(self: *Self, width: usize, height: usize) !void { - self.attached = true; - try self.client.?.registerNotifyMethod( - "redraw", - .{ - .userdata = self, - .func = &redrawCallback, - }, - ); - self.screen = try vaxis.AllocatingScreen.init(self.alloc, width, height); - self.visible_screen = try vaxis.AllocatingScreen.init(self.alloc, width, height); - const params = try Client.createParams(3, self.alloc); - defer Client.freeParams(params, self.alloc); - - var opts = znvim.Payload.mapPayload(self.alloc); - try opts.mapPut("ext_linegrid", znvim.Payload.boolToPayload(true)); - - params.arr[0] = znvim.Payload.uintToPayload(self.screen.width); - params.arr[1] = znvim.Payload.uintToPayload(self.screen.height); - params.arr[2] = opts; - - const result = try self.client.?.call("nvim_ui_attach", params); - switch (result) { - .err => |err| Client.freeParams(err, self.alloc), - .result => |r| Client.freeParams(r, self.alloc), - } - } - - fn resize(self: *Self, width: usize, height: usize) !void { - self.screen.deinit(self.alloc); - self.visible_screen.deinit(self.alloc); - self.screen = try vaxis.AllocatingScreen.init(self.alloc, width, height); - self.visible_screen = try vaxis.AllocatingScreen.init(self.alloc, width, height); - const params = try Client.createParams(2, self.alloc); - defer Client.freeParams(params, self.alloc); - - params.arr[0] = znvim.Payload.uintToPayload(self.screen.width); - params.arr[1] = znvim.Payload.uintToPayload(self.screen.height); - - try self.client.?.notify("nvim_ui_try_resize", params); - } - - fn redrawCallback( - params: znvim.Payload, - alloc: std.mem.Allocator, - userdata: ?*anyopaque, - ) void { - _ = alloc; // autofix - assert(userdata != null); - var self: *Self = @ptrCast(@alignCast(userdata.?)); - for (params.arr) |event| { - assert(event == znvim.Payload.arr); - const event_name = event.arr[0].str.value(); - log.debug("redraw callback event {s}", .{event_name}); - const event_enum = std.meta.stringToEnum(NvimEvent, event_name) orelse { - log.err("unhandled nvim event: {s}", .{event_name}); - continue; - }; - assert(event.arr[1] == znvim.Payload.arr); - self.handleEvent(event_enum, event.arr[1..]); - } - } - - fn nvimLoop(self: *Self) void { - if (self.client) |*client| { - while (true) { - client.loop() catch |err| { - log.err("rpc loop error: {}", .{err}); - self.loop.postEvent(.{ .nvim = .{ .quit = self } }); - return; - }; - } - } - } - - const NvimEvent = enum { - chdir, - default_colors_set, - flush, - grid_clear, - grid_cursor_goto, - grid_line, - grid_resize, - grid_scroll, - hl_attr_define, - hl_group_set, - mode_change, - mode_info_set, - option_set, - set_icon, - set_title, - }; - - const OptionSet = enum { - ambiwidth, - arabicshape, - emoji, - guifont, - guifontwide, - linespace, - mousefocus, - mousehide, - mousemoveevent, - pumblend, - showtabline, - termguicolors, - termsync, - ttimeout, - ttimeoutlen, - verbose, - - ext_cmdline, - ext_hlstate, - ext_linegrid, - ext_messages, - ext_multigrid, - ext_popupmenu, - ext_tabline, - ext_termcolors, - ext_wildmenu, - }; - - const Mode = struct { - cursor_style_enabled: bool = false, - cursor_shape: vaxis.Cell.CursorShape = .default, - attr_id: usize = 0, - mouse_shape: vaxis.Mouse.Shape = .default, - short_name: []const u8 = "", - name: []const u8 = "", - - fn deinit(self: Mode, alloc: std.mem.Allocator) void { - alloc.free(self.short_name); - alloc.free(self.name); - } - - const Keys = enum { - cursor_shape, - blinkon, - blinkoff, - attr_id, - short_name, - name, - mouse_shape, - }; - }; - - const HighlightMap = struct { - /// the keys used in rgb_dict field in the nvim rpc event - const Keys = enum { - foreground, - background, - special, - italic, - bold, - strikethrough, - reverse, - underline, - undercurl, - underdouble, - underdotted, - - // not used: - altfont, - blend, - url, // TODO: handle urls - - }; - - const Highlight = struct { - id: u64, - attrs: struct { - fg: ?vaxis.Color = null, - bg: ?vaxis.Color = null, - special: ?vaxis.Color = null, - italic: ?bool = null, - bold: ?bool = null, - strikethrough: ?bool = null, - underline: ?bool = null, - undercurl: ?bool = null, - underdouble: ?bool = null, - underdotted: ?bool = null, - reverse: ?bool = null, - // TODO: urls - // url: ?[]const u8 = null, - } = .{}, - }; - - alloc: std.mem.Allocator, - default: vaxis.Style, - map: std.ArrayList(Highlight), - - pub fn init(alloc: std.mem.Allocator) HighlightMap { - return .{ - .alloc = alloc, - .default = .{}, - .map = std.ArrayList(Highlight).init(alloc), - }; - } - - /// returns the requested id, or default if not found - pub fn get(self: *HighlightMap, id: u64) vaxis.Style { - for (self.map.items) |h| { - if (h.id == id) return self.merge(h); - } else return self.default; - } - - pub fn put(self: *HighlightMap, hl: Highlight) !void { - for (self.map.items, 0..) |h, i| { - if (h.id == hl.id) { - self.map.items[i] = hl; - return; - } - } else try self.map.append(hl); - } - - /// merges a Highlight with the default to create a vaxis.Style - fn merge(self: HighlightMap, hl: Highlight) vaxis.Style { - var result = self.default; - const attrs = hl.attrs; - if (attrs.fg) |val| result.fg = val; - if (attrs.bg) |val| result.bg = val; - if (attrs.special) |val| result.ul = val; - if (attrs.italic) |val| result.italic = val; - if (attrs.bold) |val| result.bold = val; - if (attrs.strikethrough) |val| result.strikethrough = val; - if (attrs.underline) |val| result.ul_style = if (val) .single else .off; - if (attrs.undercurl) |val| result.ul_style = if (val) .single else .off; - if (attrs.underdotted) |val| result.ul_style = if (val) .dotted else .off; - if (attrs.reverse) |val| result.reverse = val; - // TODO: hyperlinks - return result; - } - }; - - /// handles an nvim event. params will always be a .arr payload type. Each event - /// is of the form: [ "event_name", [ param_tuple ], [param_tuple], ... ]. Each - /// event can come with multiple params (IE multiple instances of the same event) - fn handleEvent(self: *Self, event: NvimEvent, params: []znvim.Payload) void { - switch (event) { - .chdir => { - // param_tuple: [path] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 1); - assert(param.arr[0] == znvim.Payload.str); - } - }, - .default_colors_set => { - // param_tuple: [rgb_fg, rgb_bg, rgb_sp, cterm_fg, cterm_bg] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 5); - self.hl_map.default.fg = vaxis.Color.rgbFromUint(@truncate(param.arr[0].uint)); - self.hl_map.default.bg = vaxis.Color.rgbFromUint(@truncate(param.arr[1].uint)); - self.hl_map.default.ul = vaxis.Color.rgbFromUint(@truncate(param.arr[2].uint)); - } - }, - .flush => { - self.mutex.lock(); - defer self.mutex.unlock(); - var row: usize = 0; - while (row < self.visible_screen.height) : (row += 1) { - var col: usize = 0; - while (col < self.visible_screen.width) : (col += 1) { - self.visible_screen.writeCell(col, row, self.screen.readCell(col, row).?); - } - } - self.visible_screen.cursor_row = self.screen.cursor_row; - self.visible_screen.cursor_col = self.screen.cursor_col; - self.visible_screen.cursor_vis = self.screen.cursor_vis; - self.visible_screen.cursor_shape = self.screen.cursor_shape; - if (!self.dirty) { - self.dirty = true; - self.loop.postEvent(.{ .nvim = .{ .redraw = self } }); - } - }, - .grid_clear => { - var row: usize = 0; - var col: usize = 0; - while (row < self.screen.height) : (row += 1) { - while (col < self.screen.width) : (col += 1) { - self.screen.writeCell(col, row, .{ .style = self.hl_map.default }); - } - } - }, - .grid_cursor_goto => { - // param_tuple: [grid, row, col] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 3); - self.screen.cursor_row = @truncate(param.arr[1].uint); - self.screen.cursor_col = @truncate(param.arr[2].uint); - self.screen.cursor_vis = true; - } - }, - .grid_line => { - // param_tuple: [grid, row, col_start, cells, wrap] - var style: vaxis.Style = self.hl_map.default; - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 5); - assert(param.arr[1] == znvim.Payload.uint); - assert(param.arr[2] == znvim.Payload.uint); - assert(param.arr[3] == znvim.Payload.arr); - const row: usize = param.arr[1].uint; - var col: usize = param.arr[2].uint; - const cells = param.arr[3].arr; - for (cells) |cell| { - assert(cell == znvim.Payload.arr); - switch (cell.arr.len) { - 1 => { - assert(cell.arr[0] == znvim.Payload.str); - self.screen.writeCell(col, row, .{ - .char = .{ - .grapheme = cell.arr[0].str.value(), - }, - .style = style, - }); - col += 1; - }, - 2 => { - assert(cell.arr[0] == znvim.Payload.str); - assert(cell.arr[1] == znvim.Payload.uint); - style = self.hl_map.get(cell.arr[1].uint); - self.screen.writeCell(col, row, .{ - .char = .{ - .grapheme = cell.arr[0].str.value(), - }, - .style = style, - }); - col += 1; - }, - 3 => { - assert(cell.arr[0] == znvim.Payload.str); - assert(cell.arr[1] == znvim.Payload.uint); - assert(cell.arr[2] == znvim.Payload.uint); - style = self.hl_map.get(cell.arr[1].uint); - var i: usize = 0; - while (i < cell.arr[2].uint) : (i += 1) { - self.screen.writeCell(col, row, .{ - .char = .{ - .grapheme = cell.arr[0].str.value(), - }, - .style = style, - }); - col += 1; - } - }, - else => unreachable, - } - } - } - }, - .grid_resize => { - // param_tuple: [grid, width, height] - // We don't need to handle this since we aren't activating - // ui_multigrid. Grid ID 1 will always be our main grid - }, - .grid_scroll => { - // param_tuple: [grid, top, bot, left, right, rows, cols] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 7); - // we don't care about grid - // assert(param.arr[0] == znvim.Payload.uint); - assert(param.arr[1] == znvim.Payload.uint); - assert(param.arr[2] == znvim.Payload.uint); - assert(param.arr[3] == znvim.Payload.uint); - assert(param.arr[4] == znvim.Payload.uint); - assert(param.arr[5] == znvim.Payload.uint or - param.arr[5] == znvim.Payload.int); - // we don't care about cols. This is always zero - // currently - // assert(param.arr[6] == znvim.Payload.uint); - const top: usize = @truncate(param.arr[1].uint); - const bot: usize = @truncate(param.arr[2].uint); - const left: usize = @truncate(param.arr[3].uint); - const right: usize = @truncate(param.arr[4].uint); - if (param.arr[5] == znvim.Payload.uint) { - const rows: usize = @truncate(param.arr[5].uint); - var row: usize = top; - while (row < bot) : (row += 1) { - if (row + rows > bot) break; - var col: usize = left; - while (col < right) : (col += 1) { - const cell = self.screen.readCell(col, row + rows) orelse return; - self.screen.writeCell(col, row, cell); - } - } - } else { - const rows: usize = @intCast(-param.arr[5].int); - var row: usize = bot -| 1; - while (row >= top) : (row -|= 1) { - if (row + 1 -| rows <= top) break; - var col: usize = left; - while (col < right) : (col += 1) { - const cell = self.screen.readCell(col, row -| rows) orelse unreachable; - self.screen.writeCell(col, row, cell); - } - } - } - } - }, - .hl_attr_define => { - // param_tuple: [id, rgb_attr, cterm_attr, info] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 4); - assert(param.arr[0] == znvim.Payload.uint); - assert(param.arr[1] == znvim.Payload.map); - // we don't care about cterm_attr - // assert(param.arr[2] == znvim.Payload.map); - assert(param.arr[3] == znvim.Payload.arr); - const rgb_dict = param.arr[1].map; - - var hl: HighlightMap.Highlight = .{ - .id = param.arr[0].uint, - }; - var rgb_iter = rgb_dict.iterator(); - while (rgb_iter.next()) |kv| { - const key = std.meta.stringToEnum(HighlightMap.Keys, kv.key_ptr.*) orelse { - log.warn("unhandled highlight key: {s}", .{kv.key_ptr.*}); - continue; - }; - switch (key) { - .foreground => { - hl.attrs.fg = vaxis.Color.rgbFromUint(@truncate(kv.value_ptr.*.uint)); - }, - .background => { - hl.attrs.bg = vaxis.Color.rgbFromUint(@truncate(kv.value_ptr.*.uint)); - }, - .special => { - hl.attrs.bg = vaxis.Color.rgbFromUint(@truncate(kv.value_ptr.*.uint)); - }, - .italic => hl.attrs.italic = kv.value_ptr.*.bool, - .bold => hl.attrs.bold = kv.value_ptr.*.bool, - .strikethrough => hl.attrs.strikethrough = kv.value_ptr.*.bool, - .underline => hl.attrs.underline = kv.value_ptr.*.bool, - .undercurl => hl.attrs.undercurl = kv.value_ptr.*.bool, - .underdouble => hl.attrs.underdouble = kv.value_ptr.*.bool, - .underdotted => hl.attrs.underdotted = kv.value_ptr.*.bool, - .reverse => hl.attrs.reverse = kv.value_ptr.*.bool, - else => {}, - } - } - self.hl_map.put(hl) catch |err| { - log.err("couldn't save highlight: {}", .{err}); - }; - } - }, - .hl_group_set => {}, // not used right now - .mode_change => { - // param_tuple: [mode, mode_idx] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 2); - assert(param.arr[1] == znvim.Payload.uint); - const mode = self.mode_set.items[param.arr[1].uint]; - log.debug("MODE CHANGE: {}", .{mode.cursor_shape}); - self.screen.cursor_shape = mode.cursor_shape; - } - }, - .mode_info_set => { - // param_tuple: [cursor_style_enabled, mode_info] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 2); - assert(param.arr[0] == znvim.Payload.bool); - assert(param.arr[1] == znvim.Payload.arr); - for (param.arr[1].arr) |mode_info| { - assert(mode_info == znvim.Payload.map); - var iter = mode_info.map.iterator(); - var mode: Mode = .{}; - var blink: ?bool = null; - var shape: vaxis.Cell.CursorShape = .default; - while (iter.next()) |kv| { - const key = std.meta.stringToEnum(Mode.Keys, kv.key_ptr.*) orelse continue; - switch (key) { - .cursor_shape => { - if (std.mem.eql(u8, "block", kv.value_ptr.*.str.value())) - shape = .block - else if (std.mem.eql(u8, "horizontal", kv.value_ptr.*.str.value())) - shape = .underline - else if (std.mem.eql(u8, "vertical", kv.value_ptr.*.str.value())) - shape = .beam; - }, - .short_name => {}, - .name => {}, - .mouse_shape => {}, - .attr_id => {}, - .blinkon => { - if (blink == null and kv.value_ptr.*.uint != 0) - blink = true - else - blink = false; - }, - .blinkoff => { - if (blink == null and kv.value_ptr.*.uint != 0) - blink = true - else - blink = false; - }, - } - mode.cursor_shape = if (blink == null or !blink.?) - shape - else - @enumFromInt(@intFromEnum(shape) - 1); - log.err("key={s}, value={}", .{ kv.key_ptr.*, kv.value_ptr.* }); - } - self.mode_set.append(mode) catch |err| { - log.err("couldn't add mode_set: {}", .{err}); - }; - } - } - }, - .option_set => { - // param_tuple: [name, value] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 2); - assert(param.arr[0] == znvim.Payload.str); - const opt = std.meta.stringToEnum(OptionSet, param.arr[0].str.value()) orelse { - log.err("unknonwn 'option_set' key: {s}", .{param.arr[0].str.value()}); - continue; - }; - switch (opt) { - .ambiwidth => {}, - .arabicshape => {}, - .emoji => {}, - .guifont => {}, - .guifontwide => {}, - .linespace => {}, - .mousefocus => {}, - .mousehide => {}, - .mousemoveevent => {}, - .pumblend => {}, - .showtabline => {}, - .termguicolors => {}, - .termsync => {}, - .ttimeout => {}, - .ttimeoutlen => {}, - .verbose => {}, - .ext_cmdline => {}, - .ext_hlstate => {}, - .ext_linegrid => {}, - .ext_messages => {}, - .ext_multigrid => {}, - .ext_popupmenu => {}, - .ext_tabline => {}, - .ext_termcolors => {}, - .ext_wildmenu => {}, - } - } - }, - .set_icon => { - // param_tuple: [title] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 1); - assert(param.arr[0] == znvim.Payload.str); - const icon = param.arr[0].str.value(); - log.debug("set_icon: {s}", .{icon}); - } - }, - .set_title => { - // param_tuple: [icon] - for (params) |param| { - assert(param == znvim.Payload.arr); - assert(param.arr.len == 1); - assert(param.arr[0] == znvim.Payload.str); - const title = param.arr[0].str.value(); - log.debug("set_title: {s}", .{title}); - } - }, - } - } - }; -}