From ee113c426922ee04f79e2f225674e2dbbe188df5 Mon Sep 17 00:00:00 2001 From: Jari Vetoniemi Date: Sat, 1 Jun 2024 00:23:28 +0900 Subject: [PATCH] widgets: add CodeView widget CodeView widget allows viewing code in a more visually pleasing manner. It combines LineNumbers widget to provide line numbers on the side. The CodeView widget allows you to optionally highlight a specific line and show indentation guidelines. It is not intended to be a fully fledged editor, but rather for showing code snippets to a user. While it is not a fully fledged editor, this widget's code can give you a good starting point. --- src/widgets.zig | 1 + src/widgets/CodeView.zig | 112 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 src/widgets/CodeView.zig diff --git a/src/widgets.zig b/src/widgets.zig index 5119e11..e077723 100644 --- a/src/widgets.zig +++ b/src/widgets.zig @@ -9,3 +9,4 @@ pub const nvim = @import("widgets/nvim.zig"); pub const ScrollView = @import("widgets/ScrollView.zig"); pub const LineNumbers = @import("widgets/LineNumbers.zig"); pub const TextView = @import("widgets/TextView.zig"); +pub const CodeView = @import("widgets/CodeView.zig"); diff --git a/src/widgets/CodeView.zig b/src/widgets/CodeView.zig new file mode 100644 index 0000000..7bad7a0 --- /dev/null +++ b/src/widgets/CodeView.zig @@ -0,0 +1,112 @@ +const std = @import("std"); +const vaxis = @import("../main.zig"); +const ScrollView = vaxis.widgets.ScrollView; +const LineNumbers = vaxis.widgets.LineNumbers; + +pub const DrawOptions = struct { + highlighted_line: usize = 0, + draw_line_numbers: bool = true, + indentation: usize = 0, +}; + +pub const Buffer = vaxis.widgets.TextView.Buffer; + +scroll_view: ScrollView = .{ .vertical_scrollbar = null }, +highlighted_style: vaxis.Style = .{ .bg = .{ .index = 0 } }, +indentation_cell: vaxis.Cell = .{ + .char = .{ + .grapheme = "┆", + .width = 1, + }, + .style = .{ .dim = true }, +}, + +pub fn input(self: *@This(), key: vaxis.Key) void { + self.scroll_view.input(key); +} + +pub fn draw(self: *@This(), win: vaxis.Window, buffer: Buffer, opts: DrawOptions) void { + const pad_left: usize = if (opts.draw_line_numbers) LineNumbers.numDigits(buffer.rows) +| 1 else 0; + self.scroll_view.draw(win, .{ + .cols = buffer.cols + pad_left, + .rows = buffer.rows, + }); + if (opts.draw_line_numbers) { + var nl: LineNumbers = .{ + .highlighted_line = opts.highlighted_line, + .num_lines = buffer.rows +| 1, + }; + nl.draw(win.child(.{ + .x_off = 0, + .y_off = 0, + .width = .{ .limit = pad_left }, + .height = .{ .limit = win.height }, + }), self.scroll_view.scroll.y); + } + self.drawCode(win.child(.{ .x_off = pad_left }), buffer, opts); +} + +fn drawCode(self: *@This(), win: vaxis.Window, buffer: Buffer, opts: DrawOptions) void { + const Pos = struct { x: usize = 0, y: usize = 0 }; + var pos: Pos = .{}; + var byte_index: usize = 0; + var is_indentation = true; + const bounds = self.scroll_view.bounds(win); + for (buffer.grapheme.items(.len), buffer.grapheme.items(.offset), 0..) |g_len, g_offset, index| { + if (bounds.above(pos.y)) { + break; + } + + const cluster = buffer.content.items[g_offset..][0..g_len]; + defer byte_index += cluster.len; + + if (std.mem.eql(u8, cluster, "\n")) { + if (index == buffer.grapheme.len - 1) { + break; + } + pos.y += 1; + pos.x = 0; + is_indentation = true; + continue; + } else if (bounds.below(pos.y)) { + continue; + } + + const highlighted_line = pos.y +| 1 == opts.highlighted_line; + var style: vaxis.Style = if (highlighted_line) self.highlighted_style else .{}; + + if (buffer.style_map.get(byte_index)) |meta| { + const tmp = style.bg; + style = buffer.style_list.items[meta]; + style.bg = tmp; + } + + const width = win.gwidth(cluster); + defer pos.x +|= width; + + if (!bounds.colInside(pos.x)) { + continue; + } + + if (opts.indentation > 0 and !std.mem.eql(u8, cluster, " ")) { + is_indentation = false; + } + + if (is_indentation and opts.indentation > 0 and pos.x % opts.indentation == 0) { + var cell = self.indentation_cell; + cell.style.bg = style.bg; + self.scroll_view.writeCell(win, pos.x, pos.y, cell); + } else { + self.scroll_view.writeCell(win, pos.x, pos.y, .{ + .char = .{ .grapheme = cluster, .width = width }, + .style = style, + }); + } + + if (highlighted_line) { + for (pos.x +| width..bounds.x2) |x| { + self.scroll_view.writeCell(win, x, pos.y, .{ .style = style }); + } + } + } +}