From 8964a361a7e9c69a80fa22cf912ed933a3cf8c76 Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Wed, 5 Feb 2025 22:27:58 +0100 Subject: [PATCH] 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 a839e22..79439e2 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 2e20310..7c0d287 100644 --- a/src/tui/editor_gutter.zig +++ b/src/tui/editor_gutter.zig @@ -17,6 +17,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, @@ -28,6 +29,7 @@ row: u32 = 1, line: usize = 0, linenum: bool, relative: bool, +render_style: LineNumberStyle, highlight: bool, symbols: bool, width: usize = 4, @@ -48,6 +50,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, @@ -94,6 +97,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) })) @@ -108,9 +113,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; } @@ -164,7 +168,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) { @@ -174,7 +177,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(styles.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); @@ -191,13 +195,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); @@ -297,6 +305,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; @@ -400,3 +413,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 fc6d07f..132bcc6 100644 --- a/src/tui/mainview.zig +++ b/src/tui/mainview.zig @@ -579,6 +579,22 @@ const cmds = struct { } pub const gutter_mode_next_meta: 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) {