diff --git a/README.md b/README.md index 6121cc5..bd7e61f 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,14 @@ and is my daily driver for almost everything. # Features -- **Lightning Fast** TUI with ≤6ms frame times, **low latency** input handling - and smooth **animated scrolling** +- **Lightning Fast** TUI with ≤6ms frame times, **low latency** input + handling and smooth **animated scrolling** - Intuitive UI with **tabs**, **scrollbars** and **palettes** with full **mouse** support for all UI elements - Support for more than **70 programming languages**, **zero configuration** needed, via **tree-sitter** powered syntax highlighting -- **Language Server Protocol** pre configured support for most language servers +- **Language Server Protocol** pre configured support for most language + servers - Powerful **multi-cursor** editing and integrated **clipboard history** - Powerful configurable keybinding system that supports **modal** and **non-modal** editing styles @@ -27,26 +28,29 @@ and is my daily driver for almost everything. - Hybrid rope/piece-table buffer system, edit **very large files** with **thousands of cursors** - Infinite **undo** (at least until you run out of ram) -- Full **unicode** support, including support for the kitty text sizing protocol +- Full **unicode** support, including support for the kitty text sizing + protocol - Plenty of **themes** included and support for vscode themes via the flow-themes project -- Runs on **Linux, FreeBSD, MacOS, Windows and Android** (under termux) with - easy **cross-compilation** to all supported targets +- Runs on **Linux, FreeBSD, MacOS, Windows and Android** (under termux) + with easy **cross-compilation** to all supported targets # Requirements - A modern terminal with **24bit color** and, ideally, **kitty keyboard - protocol** support. **Kitty**, **Foot** and **Ghostty** are the recommended - terminals at this time. **Zellij** also works well. Most other terminals will - work, but likely with reduced functionality. -- **NerdFont** support. Either via terminal font fallback or a patched font. + protocol** support. **Kitty**, **Foot** and **Ghostty** are the + recommended terminals at this time. **Zellij** also works well. Most + other terminals will work, but likely with reduced functionality. +- **NerdFont** support. Either via terminal font fallback or a patched + font. - A **UTF-8** locale # Roadmap -See our [devlog](https://flow-control.dev/devlog/2025/) for on-going updates from the development team. +See our [devlog](https://flow-control.dev/devlog/2025/) for on-going +updates from the development team. ## In Development @@ -63,8 +67,8 @@ See our [devlog](https://flow-control.dev/devlog/2025/) for on-going updates fro # Download / Install -There is an [installation guide](https://flow-control.dev/installation) on the -main website, and source, release and nightly build binary +There is an [installation guide](https://flow-control.dev/installation) on +the main website, and source, release and nightly build binary [downloads](https://flow-control.dev/downloads). Or check your favorite local system package repository. @@ -82,9 +86,9 @@ Flow builds with zig 0.15.2 at this time. Build with: zig build -Doptimize=ReleaseSafe ``` -Zig will by default build a binary optimized for your specific CPU. If you get -illegal instruction errors add `-Dcpu=baseline` to the build command to produce -a binary with generic CPU support. +Zig will by default build a binary optimized for your specific CPU. If you +get illegal instruction errors add `-Dcpu=baseline` to the build command to +produce a binary with generic CPU support. Thanks to Zig you may also cross-compile from any host to pretty much any @@ -105,8 +109,8 @@ The output binary is: zig-out/bin/flow ``` -It is statically built (by default) and contains all the required tree-sitter parsers -and queries. No additional runtime files are required. +It is statically built (by default) and contains all the required +tree-sitter parsers and queries. No additional runtime files are required. # Running Flow Control @@ -125,8 +129,9 @@ Or if you prefer, let zig install it in your home directory: zig build -Doptimize=ReleaseSafe --prefix ~/.local ``` -Flow Control is a single statically linked binary. No further runtime files are -required. You may install it on another system by simply copying the binary. +Flow Control is a single statically linked binary. No further runtime files +are required. You may install it on another system by simply copying the +binary. ```shell scp zig-out/bin/flow root@otherhost:/usr/local/bin @@ -138,8 +143,9 @@ Files to load may be specifed on the command line: flow fileA.zig fileB.zig ``` -The last file will be opened and the previous files will be placed in reverse -order at the top of the recent files list. Switch to recent files with Ctrl-e. +The last file will be opened and the previous files will be placed in +reverse order at the top of the recent files list. Switch to recent files +with Ctrl-e. Common target line specifiers are supported too: @@ -171,13 +177,13 @@ See `flow --help` for the full list of command line options. A basic user manual is available inside flow. You can open it with the `Open help` command (F1). -It is also available in the website [documentation](https://flow-control.dev/docs/) -section. +It is also available in the website +[documentation](https://flow-control.dev/docs/) section. ## Development Resources -Additional [developer](https://flow-control.dev/docs/#resources) resources can -be found on the Flow Control website at. +Additional [developer](https://flow-control.dev/docs/#resources) resources +can be found on the Flow Control website at. There is also an AI generated developer guide at [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/neurocyte/flow). @@ -186,23 +192,23 @@ Accuracy may vary. Check details against the referenced source code. # Configuration -Configuration is mostly dynamically maintained with various commands in the UI. -It is stored under the standard user configuration path. Usually -`~/.config/flow` on Linux. %APPDATA%\Roaming\flow on Windows. Somewhere magical -on MacOS. +Configuration is mostly dynamically maintained with various commands in the +UI. It is stored under the standard user configuration path. Usually +`~/.config/flow` on Linux. %APPDATA%\Roaming\flow on Windows. Somewhere +magical on MacOS. -There are commands to open the various configuration files, so you don't have to -manually find them. Look for commands starting with `Edit` in the command -palette. +There are commands to open the various configuration files, so you don't +have to manually find them. Look for commands starting with `Edit` in the +command palette. -File types may be configured with the `Edit file type configuration` command. -You can also create a new file type by adding a new `.conf` file to the -`file_type` directory. Have a look at an existing file type to see what options -are available. +File types may be configured with the `Edit file type configuration` +command. You can also create a new file type by adding a new `.conf` file +to the `file_type` directory. Have a look at an existing file type to see +what options are available. -Logs, traces and per-project most recently used file lists are stored in the -standard user application state directory. Usually `~/.local/state/flow` on -Linux and %APPDATA%\Roaming\flow on Windows. +Logs, traces and per-project most recently used file lists are stored in +the standard user application state directory. Usually +`~/.local/state/flow` on Linux and %APPDATA%\Roaming\flow on Windows. # Key bindings and commands @@ -213,16 +219,16 @@ Press `ctrl+shift+p` or `alt+x` to show the command palette. Press `ctrl+F2` to see a full list of all current keybindings and commands. Run the `Edit keybindings` command to save the current keybinding mode to a -file and open it for editing. Save your customized keybinds under a new name -in the same directory to create an entirely new keybinding mode. Keybinding -changes will take effect on restart. +file and open it for editing. Save your customized keybinds under a new +name in the same directory to create an entirely new keybinding mode. +Keybinding changes will take effect on restart. # Terminal configuration -Kitty, Ghostty and most other terminals have default keybindings that conflict -with common editor commands. I highly recommend rebinding them to keys that are -not generally used anywhere else. +Kitty, Ghostty and most other terminals have default keybindings that +conflict with common editor commands. I highly recommend rebinding them to +keys that are not generally used anywhere else. For Kitty rebinding `kitty_mod` is usually enough: ``` diff --git a/src/buffer/reflow.zig b/src/buffer/reflow.zig index 7cda022..55c1480 100644 --- a/src/buffer/reflow.zig +++ b/src/buffer/reflow.zig @@ -1,10 +1,15 @@ pub fn reflow(allocator: std.mem.Allocator, text: []const u8, width: usize) error{ OutOfMemory, WriteFailed }![]u8 { - const prefix = detect_prefix(text); - const words = try split_words(allocator, text, prefix.len); + const len = text.len; + const trailing_ln: bool = (len > 0 and text[len - 1] == '\n'); + const input = if (trailing_ln) text[0 .. len - 1] else text; + const prefix = detect_prefix(input); + const words = try split_words(allocator, input, prefix.len); defer allocator.free(words); var output: std.Io.Writer.Allocating = .init(allocator); const writer = &output.writer; + std.log.info("reflow @{d}", .{width}); + var first = true; var line_len: usize = 0; for (words) |word| { @@ -16,30 +21,35 @@ pub fn reflow(allocator: std.mem.Allocator, text: []const u8, width: usize) erro .begin => { if (first) { try writer.writeAll(prefix.first); + line_len += prefix.first.len; first = false; } else { try writer.writeAll(prefix.continuation); + line_len += prefix.continuation.len; var pad = prefix.first.len - prefix.continuation.len; - while (pad > 0) : (pad -= 1) + while (pad > 0) : (pad -= 1) { try writer.writeByte(' '); + line_len += 1; + } } - line_len += prefix.len; continue :blk .words; }, .words => { - if (line_len > prefix.len and line_len + word.len + 1 >= width - 1) { - try writer.writeByte('\n'); - line_len = 0; - continue :blk .begin; - } - if (line_len > prefix.len) + if (line_len > prefix.len) { + if (line_len + word.len + 1 >= width) { + try writer.writeByte('\n'); + line_len = 0; + continue :blk .begin; + } try writer.writeByte(' '); + line_len += 1; + } try writer.writeAll(word); line_len += word.len; }, } } - + if (trailing_ln) try writer.writeByte('\n'); return output.toOwnedSlice(); } @@ -61,10 +71,10 @@ fn detect_prefix(text: []const u8) Prefix { const line1 = lines.next() orelse return .{}; var prefix: []const u8 = line1; var count: usize = 0; - while (lines.next()) |line| { + while (lines.next()) |line| if (line.len > 0) { prefix = lcp(prefix, line); count += 1; - } + }; if (count < 1) return .{ .len = 0, .first = &.{}, diff --git a/src/config.zig b/src/config.zig index fe3a454..571cb70 100644 --- a/src/config.zig +++ b/src/config.zig @@ -33,6 +33,7 @@ follow_cursor_on_buffer_switch: bool = false, //scroll cursor into view on buffe default_cursor: CursorShape = .default, modes_can_change_cursor: bool = true, enable_auto_save: bool = false, +auto_save_mode: AutoSaveMode = .on_focus_change, limit_auto_save_file_types: ?[]const []const u8 = null, // null means *all* enable_prefix_keyhints: bool = true, enable_auto_find: bool = true, @@ -45,7 +46,7 @@ auto_run_commands: ?[]const []const u8 = &.{"save_session_quiet"}, // a list of indent_size: usize = 4, tab_width: usize = 8, indent_mode: IndentMode = .auto, -reflow_width: usize = 80, +reflow_width: usize = 76, top_bar: []const u8 = "tabs", bottom_bar: []const u8 = "mode file log selection diagnostics keybind branch linenumber clock spacer", @@ -149,6 +150,12 @@ pub const WhitespaceMode = enum { none, }; +pub const AutoSaveMode = enum { + on_input_idle, + on_focus_change, + on_document_change, +}; + pub const CursorShape = enum { default, block_blink, diff --git a/src/tui/editor.zig b/src/tui/editor.zig index ee77f90..65c4dea 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -649,7 +649,7 @@ pub const Editor = struct { self.highlight_references_pending.deinit(self.allocator); self.handlers.deinit(); self.logger.deinit(); - if (self.buffer) |p| self.buffer_manager.retire(p, meta.written()); + if (self.buffer) |p| self.retire_buffer(p, meta.written()); } pub fn need_render(_: *Self) void { @@ -697,7 +697,6 @@ pub const Editor = struct { defer frame.deinit(); break :blk try self.buffer_manager.open_file(file_path); }; - if (tui.config().enable_auto_save) buffer.enable_auto_save(); return self.open_buffer(file_path, buffer, null); } @@ -713,7 +712,7 @@ pub const Editor = struct { fn open_buffer(self: *Self, file_path: []const u8, new_buf: *Buffer, file_type_: ?[]const u8) !void { const frame = tracy.initZone(@src(), .{ .name = "open_buffer" }); defer frame.deinit(); - errdefer self.buffer_manager.retire(new_buf, null); + errdefer self.retire_buffer(new_buf, null); self.cancel_all_selections(); self.get_primary().reset(); self.file_path = try self.allocator.dupe(u8, file_path); @@ -755,8 +754,6 @@ pub const Editor = struct { self.checked_formatter = false; self.formatter = null; - self.maybe_enable_auto_save(); - const syn = blk: { const frame_ = tracy.initZone(@src(), .{ .name = "create" }); defer frame_.deinit(); @@ -793,7 +790,6 @@ pub const Editor = struct { buffer.file_type_icon = fti; buffer.file_type_color = ftc; } - const auto_save = if (self.buffer) |b| b.is_auto_save() and !b.is_ephemeral() else false; if (buffer_meta) |meta| { const frame_ = tracy.initZone(@src(), .{ .name = "extract_state" }); @@ -801,24 +797,29 @@ pub const Editor = struct { var iter = meta; try self.extract_state(&iter, .none); } + + const auto_save = if (self.buffer) |b| + self.maybe_enable_buffer_auto_save(b) + else + false; + try self.send_editor_open(file_path, new_buf.file_exists, ftn, fti, ftc, auto_save); } - fn maybe_enable_auto_save(self: *Self) void { - const buffer = self.buffer orelse return; - if (self.restored_state) return; - buffer.disable_auto_save(); - if (!tui.config().enable_auto_save) return; - const self_file_type = self.file_type orelse return; + fn maybe_enable_buffer_auto_save(self: *Self, buffer: *Buffer) bool { + if (self.restored_state) return false; + if (!tui.config().enable_auto_save) return false; + const self_file_type = self.file_type orelse return false; enable: { const file_types = tui.config().limit_auto_save_file_types orelse break :enable; for (file_types) |file_type| if (std.mem.eql(u8, file_type, self_file_type.name)) break :enable; - return; + return false; } buffer.enable_auto_save(); + return !buffer.is_ephemeral(); } fn detect_indent_mode(self: *Self, content: []const u8) void { @@ -853,11 +854,16 @@ pub const Editor = struct { } pub const set_editor_tab_width_meta: Meta = .{ .arguments = &.{.integer} }; + fn retire_buffer(self: *const Self, buffer: *Buffer, meta: ?[]const u8) void { + self.buffer_manager.retire(buffer, meta); + auto_save_buffer(buffer, .on_focus_change); + } + fn close(self: *Self) !void { var meta: std.Io.Writer.Allocating = .init(self.allocator); defer meta.deinit(); self.write_state(&meta.writer) catch {}; - if (self.buffer) |b_mut| self.buffer_manager.retire(b_mut, meta.written()); + if (self.buffer) |b_mut| self.retire_buffer(b_mut, meta.written()); self.cancel_all_selections(); self.buffer = null; self.plane.erase(); @@ -2020,10 +2026,11 @@ pub const Editor = struct { fn send_editor_update(self: *const Self, old_root: ?Buffer.Root, new_root: ?Buffer.Root, eol_mode: Buffer.EolMode) !void { _ = try self.handlers.msg(.{ "E", "update" }); - if (self.buffer) |buffer| if (self.syntax) |_| if (self.file_path) |file_path| if (old_root != null and new_root != null) - project_manager.did_change(file_path, buffer.lsp_version, try text_from_root(new_root, eol_mode), try text_from_root(old_root, eol_mode), eol_mode) catch {}; - if (self.buffer) |b| if (b.is_auto_save() and !b.is_ephemeral()) - tp.self_pid().send(.{ "cmd", "save_file", .{} }) catch {}; + if (self.buffer) |buffer| { + if (self.syntax) |_| if (self.file_path) |file_path| if (old_root != null and new_root != null) + project_manager.did_change(file_path, buffer.lsp_version, try text_from_root(new_root, eol_mode), try text_from_root(old_root, eol_mode), eol_mode) catch {}; + auto_save_buffer(buffer, .on_document_change); + } } pub fn vcs_content_update(self: *const Self) !void { @@ -5547,7 +5554,10 @@ pub const Editor = struct { buffer.disable_auto_save(); self.send_editor_auto_save(buffer.is_auto_save()) catch {}; if (buffer.is_auto_save()) - tp.self_pid().send(.{ "cmd", "save_file", .{} }) catch {}; + std.log.info("enabled auto save {t}", .{tui.config().auto_save_mode}) + else + std.log.info("disabled auto save", .{}); + auto_save_buffer(buffer, .on_document_change); } pub const toggle_auto_save_meta: Meta = .{ .description = "Toggle auto save" }; @@ -7135,6 +7145,7 @@ pub const EditorWidget = struct { if (self.focused) self.commands.unregister(); self.focused = false; command.executeName("enter_mode_default", .{}) catch {}; + if (self.editor.buffer) |b| auto_save_buffer(b, .on_focus_change); } pub fn update(self: *Self) void { @@ -7213,6 +7224,7 @@ pub const EditorWidget = struct { } }, }; + if (self.editor.buffer) |b| auto_save_buffer(b, .on_input_idle); return false; } else if (try m.match(.{ "whitespace_mode", tp.extract(&whitespace_mode) })) { self.editor.render_whitespace = whitespace_mode; @@ -7428,3 +7440,24 @@ fn ViewMap(T: type, default: T) type { } }; } + +pub fn auto_save_buffer(b: *Buffer, comptime event: @import("config").AutoSaveMode) void { + const auto_save = switch (tui.config().auto_save_mode) { + .on_input_idle => comptime switch (event) { + .on_input_idle, .on_focus_change => true, + .on_document_change => false, + }, + .on_document_change => comptime switch (event) { + .on_document_change => true, + .on_input_idle, .on_focus_change => false, + }, + .on_focus_change => comptime switch (event) { + .on_focus_change => true, + .on_input_idle, .on_document_change => false, + }, + }; + if (auto_save and b.is_auto_save() and !b.is_ephemeral() and b.is_dirty()) { + tp.self_pid().send(.{ "cmd", "save_buffer", .{ b.get_file_path(), "auto_save" } }) catch {}; + std.log.debug("auto save {t} {s}", .{ event, b.get_file_path() }); + } +} diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 6b18f33..eb6b8c2 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -188,10 +188,18 @@ pub fn receive(self: *Self, from_: tp.pid_ref, m: tp.message) error{Exit}!bool { } else if (try m.match(.{ "navigate_complete", tp.extract(&path), tp.extract(&goto_args), tp.null_, tp.null_ })) { cmds.navigate_complete(self, null, path, goto_args, null, null, null) catch |e| return tp.exit_error(e, @errorReturnTrace()); return true; + } else if (try m.match(.{"focus_out"})) { + self.process_focus_out() catch |e| return tp.exit_error(e, @errorReturnTrace()); } return if (try self.floating_views.send(from_, m)) true else self.widgets.send(from_, m); } +fn process_focus_out(self: *Self) error{OutOfMemory}!void { + const buffers = try self.buffer_manager.list_unordered(self.allocator); + defer self.allocator.free(buffers); + for (buffers) |b| ed.auto_save_buffer(b, .on_focus_change); +} + pub fn update(self: *Self) void { self.widgets.update(); self.floating_views.update(); @@ -769,8 +777,11 @@ const cmds = struct { pub const create_new_file_meta: Meta = .{ .description = "New file" }; pub fn save_buffer(self: *Self, ctx: Ctx) Result { + var auto_save: bool = false; var file_path: []const u8 = undefined; - if (!(ctx.args.match(.{tp.extract(&file_path)}) catch false)) + if (ctx.args.match(.{ tp.extract(&file_path), "auto_save" }) catch false) { + auto_save = true; + } else if (!(ctx.args.match(.{tp.extract(&file_path)}) catch false)) return error.InvalidSaveBufferArgument; const buffer = self.buffer_manager.get_buffer_for_file(file_path) orelse return; @@ -786,7 +797,10 @@ const cmds = struct { const logger = log.logger("buffer"); defer logger.deinit(); if (buffer.is_ephemeral()) return logger.print_err("save", "ephemeral buffer, use save as", .{}); - if (!buffer.is_dirty()) return logger.print("no changes to save", .{}); + if (!buffer.is_dirty()) { + if (!auto_save) logger.print("no changes to save", .{}); + return; + } try buffer.store_to_file_and_clean(file_path); } pub const save_buffer_meta: Meta = .{ .arguments = &.{.string} }; diff --git a/src/tui/status/filestate.zig b/src/tui/status/filestate.zig index 3b6e8da..3425d59 100644 --- a/src/tui/status/filestate.zig +++ b/src/tui/status/filestate.zig @@ -83,6 +83,10 @@ pub fn layout(_: *Self, _: *ButtonType) Widget.Layout { } pub fn render(self: *Self, btn: *ButtonType, theme: *const Widget.Theme) bool { + const auto_save = if (self.auto_save) switch (tui.config().auto_save_mode) { + .on_input_idle, .on_document_change => true, + .on_focus_change => false, + } else false; const style_base = theme.statusbar; const style_label = if (btn.active) theme.editor_cursor else style_base; btn.plane.set_base_style(theme.editor); @@ -99,9 +103,9 @@ pub fn render(self: *Self, btn: *ButtonType, theme: *const Widget.Theme) bool { if (tui.mini_mode()) |_| render_mini_mode(&btn.plane, theme) else if (self.detailed) - self.render_detailed(&btn.plane, theme) + self.render_detailed(&btn.plane, theme, auto_save) else - self.render_normal(&btn.plane, theme); + self.render_normal(&btn.plane, theme, auto_save); self.render_terminal_title(); return false; } @@ -135,19 +139,19 @@ fn render_mini_mode(plane: *Plane, theme: *const Widget.Theme) void { // 󱣪 Content save check // 󱑛 Content save cog // 󰆔 Content save all -fn render_normal(self: *Self, plane: *Plane, theme: *const Widget.Theme) void { +fn render_normal(self: *Self, plane: *Plane, theme: *const Widget.Theme, auto_save: bool) void { plane.on_styles(styles.italic); _ = plane.putstr(" ") catch {}; if (self.file_icon.len > 0 and tui.config().show_fileicons) { self.render_file_icon(plane, theme); _ = plane.print(" ", .{}) catch {}; } - _ = plane.putstr(if (!self.file_exists) "󰽂 " else if (self.auto_save) "󱑛 " else if (self.file_dirty) "󰆓 " else "") catch {}; + _ = plane.putstr(if (!self.file_exists) "󰽂 " else if (auto_save) "󱑛 " else if (self.file_dirty) "󰆓 " else "") catch {}; _ = plane.print("{s}", .{self.name}) catch {}; return; } -fn render_detailed(self: *Self, plane: *Plane, theme: *const Widget.Theme) void { +fn render_detailed(self: *Self, plane: *Plane, theme: *const Widget.Theme, auto_save: bool) void { plane.on_styles(styles.italic); _ = plane.putstr(" ") catch {}; if (self.file_icon.len > 0 and tui.config().show_fileicons) { @@ -167,7 +171,7 @@ fn render_detailed(self: *Self, plane: *Plane, theme: *const Widget.Theme) void .tabs => "[⭾ = ␉]", }; - _ = plane.putstr(if (!self.file_exists) "󰽂" else if (self.auto_save) "󱑛" else if (self.file_dirty) "󰆓" else "󱣪") catch {}; + _ = plane.putstr(if (!self.file_exists) "󰽂" else if (auto_save) "󱑛" else if (self.file_dirty) "󰆓" else "󱣪") catch {}; _ = plane.print(" {s}:{d}:{d}", .{ self.name, self.line + 1, self.column + 1 }) catch {}; _ = plane.print(" of {d} lines", .{self.lines}) catch {}; if (self.file_type.len > 0) diff --git a/src/tui/status/tabs.zig b/src/tui/status/tabs.zig index 50f2816..65f1ce0 100644 --- a/src/tui/status/tabs.zig +++ b/src/tui/status/tabs.zig @@ -902,7 +902,10 @@ const Tab = struct { const buffer_manager = tui.get_buffer_manager() orelse @panic("tabs no buffer manager"); const buffer_ = buffer_manager.buffer_from_ref(self.buffer_ref); const is_dirty = if (buffer_) |buffer| buffer.is_dirty() else false; - const auto_save = if (buffer_) |buffer| buffer.is_auto_save() else false; + const auto_save = if (buffer_) |buffer| if (buffer.is_auto_save()) switch (tui.config().auto_save_mode) { + .on_input_idle, .on_document_change => true, + .on_focus_change => false, + } else false else false; self.render_padding(plane, .left); if (self.tab_style.file_type_icon) if (buffer_) |buffer| if (buffer.file_type_icon) |icon| { const color_: ?u24 = if (buffer.file_type_color) |color| if (!(color == 0xFFFFFF or color == 0x000000 or color == 0x000001)) color else null else null; diff --git a/src/tui/tui.zig b/src/tui/tui.zig index 7c816e3..d6c196e 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -499,12 +499,6 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { if (try m.match(.{"mouse_leave"})) return; - if (try m.match(.{"focus_in"})) - return; - - if (try m.match(.{"focus_out"})) - return; - if (try m.match(.{ "K", tp.more })) return; @@ -514,6 +508,12 @@ fn receive_safe(self: *Self, from: tp.pid_ref, m: tp.message) !void { if (try self.send_widgets(from, m)) return; + if (try m.match(.{"focus_in"})) + return; + + if (try m.match(.{"focus_out"})) + return; + if (try m.match(.{ "exit", tp.more })) { if (try m.match(.{ tp.string, "normal" }) or try m.match(.{ tp.string, "timeout_error", 125, "Operation aborted." }) or