From 100640beafd8e89d4cd6dc771246940127b48e8c Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 5 Feb 2025 16:37:03 +0100 Subject: [PATCH 1/7] fix(editor): update cursor target column after nudge_insert/_delete --- src/buffer/Cursor.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/buffer/Cursor.zig b/src/buffer/Cursor.zig index 8c68265..43df3d2 100644 --- a/src/buffer/Cursor.zig +++ b/src/buffer/Cursor.zig @@ -210,6 +210,7 @@ pub fn nudge_insert(self: *Self, nudge: Selection) void { } else { self.row += rows; } + self.target = self.col; } pub fn nudge_delete(self: *Self, nudge: Selection) bool { @@ -222,6 +223,7 @@ pub fn nudge_delete(self: *Self, nudge: Selection) bool { return false; } self.col -= nudge.end.col - nudge.begin.col; + self.target = self.col; return true; } } @@ -230,6 +232,7 @@ pub fn nudge_delete(self: *Self, nudge: Selection) bool { if (self.col < nudge.end.col) return false; self.row -= nudge.end.row - nudge.begin.row; self.col -= nudge.end.col; + self.target = self.col; return true; } self.row -= nudge.end.row - nudge.begin.row; From c4986d59008cfcc240bfb5948ee0d77c541c4514 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 5 Feb 2025 16:41:01 +0100 Subject: [PATCH 2/7] docs(fonts): add some recommended font download urls to font test text --- src/tui/fonts.zig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tui/fonts.zig b/src/tui/fonts.zig index 4bf15f8..4fe1aa3 100644 --- a/src/tui/fonts.zig +++ b/src/tui/fonts.zig @@ -223,4 +223,10 @@ pub const font_test_text: []const u8 = \\ \\🙂‍↔ \\ + \\ + \\ recommended fonts for terminals with no nerdfont fallback support (e.g. flow-gui): + \\ + \\ "IosevkaTerm Nerd Font" => https://github.com/ryanoasis/nerd-fonts/releases/download/v3.3.0/IosevkaTerm.zip + \\ "Cascadia Code NF" => https://github.com/microsoft/cascadia-code/releases/download/v2407.24/CascadiaCode-2407.24.zip + \\ ; From b3a10d73461f773dfc0bdac347f289ad387eea10 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 5 Feb 2025 22:27:58 +0100 Subject: [PATCH 3/7] feat: add line number styles --- src/config.zig | 8 ++++ src/keybind/builtin/flow.json | 1 + src/tui/editor_gutter.zig | 71 +++++++++++++++++++++++++++++++---- src/tui/mainview.zig | 16 ++++++++ 4 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/config.zig b/src/config.zig index 33a4f21..8597177 100644 --- a/src/config.zig +++ b/src/config.zig @@ -5,6 +5,7 @@ theme: []const u8 = "default", input_mode: []const u8 = "flow", gutter_line_numbers: bool = true, gutter_line_numbers_relative: bool = false, +gutter_line_numbers_style: LineNumberStyle = .ascii, gutter_symbols: bool = true, enable_terminal_cursor: bool = true, enable_terminal_color_scheme: bool = builtin.os.tag != .windows, @@ -25,3 +26,10 @@ bottom_bar: []const u8 = "mode file log selection diagnostics keybind linenumber lsp_request_timeout: usize = 10, include_files: []const u8 = "", + +pub const LineNumberStyle = enum { + ascii, + digital, + subscript, + superscript, +}; diff --git a/src/keybind/builtin/flow.json b/src/keybind/builtin/flow.json index 340793f..a9d8e89 100644 --- a/src/keybind/builtin/flow.json +++ b/src/keybind/builtin/flow.json @@ -99,6 +99,7 @@ ["alt+down", "pull_down"], ["alt+enter", "insert_line"], ["alt+f10", "gutter_mode_next"], + ["alt+shift+f10", "gutter_style_next"], ["alt+f12", "goto_declaration"], ["alt+shift+p", "open_command_palette"], ["alt+shift+d", "dupe_up"], diff --git a/src/tui/editor_gutter.zig b/src/tui/editor_gutter.zig index 6d63821..f9af227 100644 --- a/src/tui/editor_gutter.zig +++ b/src/tui/editor_gutter.zig @@ -16,6 +16,7 @@ const Widget = @import("Widget.zig"); const MessageFilter = @import("MessageFilter.zig"); const tui = @import("tui.zig"); const ed = @import("editor.zig"); +const LineNumberStyle = @import("config").LineNumberStyle; allocator: Allocator, plane: Plane, @@ -27,6 +28,7 @@ row: u32 = 1, line: usize = 0, linenum: bool, relative: bool, +render_style: LineNumberStyle, highlight: bool, symbols: bool, width: usize = 4, @@ -47,6 +49,7 @@ pub fn create(allocator: Allocator, parent: Widget, event_source: Widget, editor .parent = parent, .linenum = tui.config().gutter_line_numbers, .relative = tui.config().gutter_line_numbers_relative, + .render_style = tui.config().gutter_line_numbers_style, .highlight = tui.config().highlight_current_line_gutter, .symbols = tui.config().gutter_symbols, .editor = editor, @@ -93,6 +96,8 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { if (try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON1), tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) })) return self.primary_click(y); + if (try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON2), tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) })) + return self.middle_click(); if (try m.match(.{ "B", input.event.press, @intFromEnum(input.mouse.BUTTON3), tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) })) return self.secondary_click(); if (try m.match(.{ "D", input.event.press, @intFromEnum(input.mouse.BUTTON1), tp.any, tp.any, tp.extract(&y), tp.any, tp.extract(&ypx) })) @@ -107,9 +112,8 @@ pub fn receive(self: *Self, _: tp.pid_ref, m: tp.message) error{Exit}!bool { fn update_width(self: *Self) void { if (!self.linenum) return; - var buf: [31]u8 = undefined; - const tmp = std.fmt.bufPrint(&buf, "{d}", .{self.lines}) catch return; - self.width = if (self.relative and tmp.len > 4) 4 else @max(tmp.len, 2); + const width = int_width(self.lines); + self.width = if (self.relative and width > 4) 4 else @max(width, 2); self.width += if (self.symbols) 3 else 1; } @@ -163,7 +167,6 @@ pub fn render_linear(self: *Self, theme: *const Widget.Theme) void { var linenum = self.row + 1; var rows = self.rows; var diff_symbols = self.diff_symbols.items; - var buf: [31:0]u8 = undefined; while (rows > 0) : (rows -= 1) { if (linenum > self.lines) return; if (linenum == self.line + 1) { @@ -173,7 +176,8 @@ pub fn render_linear(self: *Self, theme: *const Widget.Theme) void { self.plane.set_style(.{ .fg = theme.editor_gutter.fg }); self.plane.off_styles(style.bold); } - _ = self.plane.print_aligned_right(@intCast(pos), "{s}", .{std.fmt.bufPrintZ(&buf, "{d} ", .{linenum}) catch return}) catch {}; + try self.plane.cursor_move_yx(@intCast(pos), 0); + try self.print_digits(linenum, self.render_style); if (self.highlight and linenum == self.line + 1) self.render_line_highlight(pos, theme); self.render_diff_symbols(&diff_symbols, pos, linenum, theme); @@ -190,13 +194,17 @@ pub fn render_relative(self: *Self, theme: *const Widget.Theme) void { var abs_linenum = self.row + 1; var rows = self.rows; var diff_symbols = self.diff_symbols.items; - var buf: [31:0]u8 = undefined; while (rows > 0) : (rows -= 1) { if (pos > self.lines - @as(u32, @intCast(row))) return; self.plane.set_style(if (linenum == 0) theme.editor_gutter_active else theme.editor_gutter); const val = @abs(if (linenum == 0) line else linenum); - const fmt = std.fmt.bufPrintZ(&buf, "{d} ", .{val}) catch return; - _ = self.plane.print_aligned_right(@intCast(pos), "{s}", .{if (fmt.len > 6) "==> " else fmt}) catch {}; + + try self.plane.cursor_move_yx(@intCast(pos), 0); + if (val > 999999) + _ = self.plane.print_aligned_right(@intCast(pos), "==> ", .{}) catch {} + else + self.print_digits(val, self.render_style) catch {}; + if (self.highlight and linenum == 0) self.render_line_highlight(pos, theme); self.render_diff_symbols(&diff_symbols, pos, abs_linenum, theme); @@ -296,6 +304,11 @@ fn secondary_click(_: *Self) error{Exit}!bool { return true; } +fn middle_click(_: *Self) error{Exit}!bool { + try command.executeName("gutter_style_next", .{}); + return true; +} + fn mouse_click_button4(_: *Self) error{Exit}!bool { try command.executeName("scroll_up_pageup", .{}); return true; @@ -399,3 +412,45 @@ pub fn filter_receive(self: *Self, _: tp.pid_ref, m: tp.message) MessageFilter.E } return false; } + +fn int_width(n_: usize) usize { + var n = n_; + var size: usize = 1; + while (true) { + n /= 10; + if (n == 0) return size; + size += 1; + } +} + +fn print_digits(self: *Self, n_: anytype, style_: LineNumberStyle) !void { + var n = n_; + var buf: [12][]const u8 = undefined; + var digits: std.ArrayListUnmanaged([]const u8) = .initBuffer(&buf); + while (true) { + digits.addOneAssumeCapacity().* = get_digit(n % 10, style_); + n /= 10; + if (n == 0) break; + } + std.mem.reverse([]const u8, digits.items); + try self.plane.cursor_move_yx(@intCast(self.plane.cursor_y()), @intCast(self.width - digits.items.len - 1)); + for (digits.items) |digit| _ = try self.plane.putstr(digit); +} + +pub fn print_digit(plane: *Plane, n: anytype, style_: LineNumberStyle) !void { + _ = try plane.putstr(get_digit(n, style_)); +} + +fn get_digit(n: anytype, style_: LineNumberStyle) []const u8 { + return switch (style_) { + .ascii => digits_ascii[n], + .digital => digits_digtl[n], + .subscript => digits_subsc[n], + .superscript => digits_super[n], + }; +} + +const digits_ascii: [10][]const u8 = .{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +const digits_digtl: [10][]const u8 = .{ "🯰", "🯱", "🯲", "🯳", "🯴", "🯵", "🯶", "🯷", "🯸", "🯹" }; +const digits_subsc: [10][]const u8 = .{ "₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉" }; +const digits_super: [10][]const u8 = .{ "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" }; diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index 97aa051..df43748 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -578,6 +578,22 @@ const cmds = struct { } pub const gutter_mode_next_meta = .{ .description = "Next gutter mode" }; + pub fn gutter_style_next(self: *Self, _: Ctx) Result { + const config = tui.config_mut(); + config.gutter_line_numbers_style = switch (config.gutter_line_numbers_style) { + .ascii => .digital, + .digital => .subscript, + .subscript => .superscript, + .superscript => .ascii, + }; + try tui.save_config(); + if (self.widgets.get("editor_gutter")) |gutter_widget| { + const gutter = gutter_widget.dynamic_cast(@import("editor_gutter.zig")) orelse return; + gutter.render_style = config.gutter_line_numbers_style; + } + } + pub const gutter_style_next_meta: Meta = .{ .description = "Next line number style" }; + pub fn goto_next_file_or_diagnostic(self: *Self, ctx: Ctx) Result { if (self.is_panel_view_showing(filelist_view)) { switch (self.file_list_type) { From 812dc85bdc516f224b6237f4e2f5d80f6517e013 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 5 Feb 2025 22:49:50 +0100 Subject: [PATCH 4/7] refactor: move digit styles to fonts.zig --- src/config.zig | 4 ++-- src/tui/editor_gutter.zig | 22 +++++----------------- src/tui/fonts.zig | 16 ++++++++++++++++ 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/config.zig b/src/config.zig index 8597177..3f9f50d 100644 --- a/src/config.zig +++ b/src/config.zig @@ -5,7 +5,7 @@ theme: []const u8 = "default", input_mode: []const u8 = "flow", gutter_line_numbers: bool = true, gutter_line_numbers_relative: bool = false, -gutter_line_numbers_style: LineNumberStyle = .ascii, +gutter_line_numbers_style: DigitStyle = .ascii, gutter_symbols: bool = true, enable_terminal_cursor: bool = true, enable_terminal_color_scheme: bool = builtin.os.tag != .windows, @@ -27,7 +27,7 @@ lsp_request_timeout: usize = 10, include_files: []const u8 = "", -pub const LineNumberStyle = enum { +pub const DigitStyle = enum { ascii, digital, subscript, diff --git a/src/tui/editor_gutter.zig b/src/tui/editor_gutter.zig index f9af227..cfe470b 100644 --- a/src/tui/editor_gutter.zig +++ b/src/tui/editor_gutter.zig @@ -16,7 +16,7 @@ const Widget = @import("Widget.zig"); const MessageFilter = @import("MessageFilter.zig"); const tui = @import("tui.zig"); const ed = @import("editor.zig"); -const LineNumberStyle = @import("config").LineNumberStyle; +const DigitStyle = @import("config").DigitStyle; allocator: Allocator, plane: Plane, @@ -28,7 +28,7 @@ row: u32 = 1, line: usize = 0, linenum: bool, relative: bool, -render_style: LineNumberStyle, +render_style: DigitStyle, highlight: bool, symbols: bool, width: usize = 4, @@ -423,7 +423,7 @@ fn int_width(n_: usize) usize { } } -fn print_digits(self: *Self, n_: anytype, style_: LineNumberStyle) !void { +fn print_digits(self: *Self, n_: anytype, style_: DigitStyle) !void { var n = n_; var buf: [12][]const u8 = undefined; var digits: std.ArrayListUnmanaged([]const u8) = .initBuffer(&buf); @@ -437,20 +437,8 @@ fn print_digits(self: *Self, n_: anytype, style_: LineNumberStyle) !void { for (digits.items) |digit| _ = try self.plane.putstr(digit); } -pub fn print_digit(plane: *Plane, n: anytype, style_: LineNumberStyle) !void { +pub fn print_digit(plane: *Plane, n: anytype, style_: DigitStyle) !void { _ = try plane.putstr(get_digit(n, style_)); } -fn get_digit(n: anytype, style_: LineNumberStyle) []const u8 { - return switch (style_) { - .ascii => digits_ascii[n], - .digital => digits_digtl[n], - .subscript => digits_subsc[n], - .superscript => digits_super[n], - }; -} - -const digits_ascii: [10][]const u8 = .{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; -const digits_digtl: [10][]const u8 = .{ "🯰", "🯱", "🯲", "🯳", "🯴", "🯵", "🯶", "🯷", "🯸", "🯹" }; -const digits_subsc: [10][]const u8 = .{ "₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉" }; -const digits_super: [10][]const u8 = .{ "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" }; +const get_digit = @import("fonts.zig").get_digit; diff --git a/src/tui/fonts.zig b/src/tui/fonts.zig index 4fe1aa3..c19685e 100644 --- a/src/tui/fonts.zig +++ b/src/tui/fonts.zig @@ -230,3 +230,19 @@ pub const font_test_text: []const u8 = \\ "Cascadia Code NF" => https://github.com/microsoft/cascadia-code/releases/download/v2407.24/CascadiaCode-2407.24.zip \\ ; + +pub const DigitStyle = @import("config").DigitStyle; + +pub fn get_digit(n: anytype, style_: DigitStyle) []const u8 { + return switch (style_) { + .ascii => digits_ascii[n], + .digital => digits_digtl[n], + .subscript => digits_subsc[n], + .superscript => digits_super[n], + }; +} + +const digits_ascii: [10][]const u8 = .{ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +const digits_digtl: [10][]const u8 = .{ "🯰", "🯱", "🯲", "🯳", "🯴", "🯵", "🯶", "🯷", "🯸", "🯹" }; +const digits_subsc: [10][]const u8 = .{ "₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉" }; +const digits_super: [10][]const u8 = .{ "⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹" }; From c186264eac1b8d6ac528384e36726b12ab1803b1 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 6 Feb 2025 10:55:42 +0100 Subject: [PATCH 5/7] feat: add configuration and command to toggle display of inline diagnostics --- src/config.zig | 1 + src/tui/editor.zig | 3 ++- src/tui/mainview.zig | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/config.zig b/src/config.zig index 3f9f50d..8f74b58 100644 --- a/src/config.zig +++ b/src/config.zig @@ -12,6 +12,7 @@ enable_terminal_color_scheme: bool = builtin.os.tag != .windows, highlight_current_line: bool = true, highlight_current_line_gutter: bool = true, whitespace_mode: []const u8 = "none", +inline_diagnostics: bool = true, animation_min_lag: usize = 0, //milliseconds animation_max_lag: usize = 150, //milliseconds enable_format_on_save: bool = false, diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 92e3121..a89c603 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -1009,7 +1009,8 @@ pub const Editor = struct { self.render_syntax(theme, cache, root) catch {}; self.render_cursors(theme, ctx_.cell_map) catch {}; self.render_whitespace_map(theme, ctx_.cell_map) catch {}; - self.render_diagnostics(theme, hl_row, ctx_.cell_map) catch {}; + if (tui.config().inline_diagnostics) + self.render_diagnostics(theme, hl_row, ctx_.cell_map) catch {}; } fn render_cursors(self: *Self, theme: *const Widget.Theme, cell_map: CellMap) !void { diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index df43748..e998943 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -594,6 +594,13 @@ const cmds = struct { } pub const gutter_style_next_meta: Meta = .{ .description = "Next line number style" }; + pub fn toggle_inline_diagnostics(_: *Self, _: Ctx) Result { + const config = tui.config_mut(); + config.inline_diagnostics = !config.inline_diagnostics; + try tui.save_config(); + } + pub const toggle_inline_diagnostics_meta: Meta = .{ .description = "Toggle display of diagnostics inline" }; + pub fn goto_next_file_or_diagnostic(self: *Self, ctx: Ctx) Result { if (self.is_panel_view_showing(filelist_view)) { switch (self.file_list_type) { From 9c01402f150b62dbc3167e14a9163aa4518082af Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 6 Feb 2025 21:18:07 +0100 Subject: [PATCH 6/7] fix: partially backport zig-0.14 command.Metadata change --- src/command.zig | 2 +- src/tui/mainview.zig | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/command.zig b/src/command.zig index 71b1ea4..44de9e6 100644 --- a/src/command.zig +++ b/src/command.zig @@ -26,7 +26,7 @@ const Vtable = struct { meta: Metadata, }; -const Metadata = struct { +pub const Metadata = struct { description: []const u8 = &[_]u8{}, arguments: []const ArgumentType = &[_]ArgumentType{}, }; diff --git a/src/tui/mainview.zig b/src/tui/mainview.zig index e998943..b4d588f 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -241,6 +241,7 @@ fn check_all_not_dirty(self: *const Self) command.Result { const cmds = struct { pub const Target = Self; const Ctx = command.Context; + const Meta = command.Metadata; const Result = command.Result; pub fn quit(self: *Self, _: Ctx) Result { From f225c9c045465d1a577a141eb3c7a7bff70cdac2 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Thu, 6 Feb 2025 21:18:43 +0100 Subject: [PATCH 7/7] fix: zig-0.13 build of editor_gutter --- src/tui/editor_gutter.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tui/editor_gutter.zig b/src/tui/editor_gutter.zig index cfe470b..1ae4d0d 100644 --- a/src/tui/editor_gutter.zig +++ b/src/tui/editor_gutter.zig @@ -426,7 +426,7 @@ fn int_width(n_: usize) usize { fn print_digits(self: *Self, n_: anytype, style_: DigitStyle) !void { var n = n_; var buf: [12][]const u8 = undefined; - var digits: std.ArrayListUnmanaged([]const u8) = .initBuffer(&buf); + var digits = std.ArrayListUnmanaged([]const u8).initBuffer(&buf); while (true) { digits.addOneAssumeCapacity().* = get_digit(n % 10, style_); n /= 10;