diff --git a/src/app.zig b/src/app.zig index c531e36..6cee964 100644 --- a/src/app.zig +++ b/src/app.zig @@ -32,7 +32,7 @@ const log = std.log.scoped(.app); /// const zterm = @import("zterm"); /// const App = zterm.App( /// union(enum) {}, -/// zterm.Renderer.Plain, +/// zterm.Renderer.Direct, /// true, /// ); /// // later on use diff --git a/src/layout/Margin.zig b/src/layout/Margin.zig index b83b9e1..8d4feb5 100644 --- a/src/layout/Margin.zig +++ b/src/layout/Margin.zig @@ -9,7 +9,7 @@ const isTaggedUnion = @import("../event.zig").isTaggedUnion; const Error = @import("../event.zig").Error; const Key = terminal.Key; -const log = std.log.scoped(.layout_marging); +const log = std.log.scoped(.layout_margin); pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: type) type { if (!isTaggedUnion(Event)) { @@ -32,7 +32,6 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t events: Events = undefined, config: Config = undefined, - // TODO: marging (for all 4 directions - relative!) const Config = struct { margin: ?u8 = undefined, left: u8 = 0, diff --git a/src/layout/Padding.zig b/src/layout/Padding.zig index d1fbd92..65295e5 100644 --- a/src/layout/Padding.zig +++ b/src/layout/Padding.zig @@ -32,7 +32,6 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t events: Events = undefined, config: Config = undefined, - // TODO: padding (for all 4 directions?) const Config = struct { padding: ?u16 = undefined, left: u16 = 0, diff --git a/src/main.zig b/src/main.zig index 32d557a..7289587 100644 --- a/src/main.zig +++ b/src/main.zig @@ -26,9 +26,8 @@ pub fn main() !void { const allocator = gpa.allocator(); var app: App = .{}; - var renderer = App.Renderer.init(allocator); - defer renderer.deinit(); - // FIX: when not running fullscreen, the application needs to screen down accordingly to display the contents + var renderer: App.Renderer = .{}; + // TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents // -> size hint how much should it use? // var layout = Layout.createFrom(layout: { @@ -205,6 +204,5 @@ pub fn main() !void { app.postEvent(e); } try layout.render(&renderer); - try renderer.flush(); } } diff --git a/src/render.zig b/src/render.zig index 0772e10..a1e8db4 100644 --- a/src/render.zig +++ b/src/render.zig @@ -1,11 +1,8 @@ //! Renderer which holds the screen to compare with the previous screen for efficient rendering. //! Each renderer should at least implement these functions: -//! - init(allocator: std.mem.Allocator) @This() {} -//! - deinit(this: *@This()) void {} //! - resize(this: *@This(), size: Size) void {} //! - clear(this: *@This(), size: Size) !void {} //! - render(this: *@This(), size: Size, contents: []u8) !void {} -//! - flush(this: @This()) !void {} //! //! Each `Renderer` should be able to be used interchangeable without having to //! change any code of any `Layout` or `Widget`. The only change should be the @@ -13,117 +10,15 @@ const std = @import("std"); const terminal = @import("terminal.zig"); -const Contents = std.ArrayList(u8); // TODO: this may contain more than just a single character! (i.e. styled) const Cells = []const terminal.Cell; const Position = terminal.Position; const Size = terminal.Size; -pub fn Buffered(comptime _: bool) type { - const log = std.log.scoped(.renderer_buffered); - return struct { - size: terminal.Size = undefined, - frame: Contents = undefined, - - const EMPTY: u8 = 0; - - pub fn init(allocator: std.mem.Allocator) @This() { - _ = log; - return .{ - .frame = Contents.init(allocator), - }; - } - - pub fn deinit(this: *@This()) void { - this.frame.deinit(); - this.* = undefined; - } - - pub fn resize(this: *@This(), size: Size) void { - std.debug.assert(size.anchor.col == 1 and size.anchor.row == 1); - this.size = size; - this.frame.resize(size.cols * size.rows) catch @panic("OOM"); - } - - pub fn clear(this: *@This(), size: Size) error{}!void { - for (size.anchor.row..size.anchor.row + size.rows) |row| { - const y = this.size.cols * (row - 1); - for (size.anchor.col..size.anchor.col + size.cols) |col| { - // log.debug("clearing index[{d}]/[{d}] at .{{ .col = {d}, .row = {d} }}", .{ y + col - 1, this.size.cols * this.size.rows, col, row }); - std.debug.assert(y + col - 1 < this.frame.items.len); - this.frame.items[y + col - 1] = EMPTY; - } - } - } - - pub fn render(this: *@This(), size: Size, contents: []u8) !void { - var c_idx: usize = 0; - row: for (size.anchor.row..size.anchor.row + size.rows) |row| { - var fill_empty = false; - const y = this.size.cols * (row - 1); - for (size.anchor.col..size.anchor.col + size.cols) |col| { - std.debug.assert(y + col - 1 < this.frame.items.len); - const idx = y + col - 1; - const item = contents[c_idx]; - if (item == '\n') { // do not print newlines - // reached end of line - // fill rest of col's of this row with EMPTY - fill_empty = true; - } - if (fill_empty) { - this.frame.items[idx] = EMPTY; - } else { - this.frame.items[idx] = item; - c_idx += 1; - } - } - // printed row - // are there still items for this row (even though there is no space left?) - if (!fill_empty) { - // find end of line and update c_idx accordingly - for (c_idx..contents.len) |c| { - if (contents[c] == '\n') { - c_idx = c + 1; - continue :row; - } - } - } else { - c_idx += 1; // skip over '\n' - } - } - } - - pub fn flush(this: @This()) !void { - try terminal.clearScreen(); - for (this.size.anchor.row..this.size.anchor.row + this.size.rows) |row| { - const y = this.size.cols * (row - 1); - for (1..this.size.cols) |col| { - std.debug.assert(y + col - 1 < this.frame.items.len); - const item = this.frame.items[y + col - 1]; - if (item == EMPTY) { - continue; - } - const pos: Position = .{ .col = @truncate(col), .row = @truncate(row) }; - try terminal.setCursorPosition(pos); - _ = try terminal.write(&.{item}); - } - } - } - }; -} - -pub fn Direct(comptime _: bool) type { +pub fn Direct(comptime fullscreen: bool) type { const log = std.log.scoped(.renderer_direct); + _ = log; + _ = fullscreen; return struct { - pub fn init(allocator: std.mem.Allocator) @This() { - _ = allocator; - _ = log; - return .{}; - } - - pub fn deinit(this: *@This()) void { - this.* = undefined; - } - pub fn resize(this: *@This(), size: Size) void { _ = this; _ = size; @@ -206,9 +101,5 @@ pub fn Direct(comptime _: bool) type { } } } - - pub fn flush(this: @This()) !void { - _ = this; - } }; } diff --git a/src/widget/node2buffer.zig b/src/widget/node2buffer.zig deleted file mode 100644 index dfa225c..0000000 --- a/src/widget/node2buffer.zig +++ /dev/null @@ -1,314 +0,0 @@ -///! Transform a given file type into a buffer which can be used by any `vaxis.widgets.View` -///! -///! Use the `toBuffer` method of any struct to convert the file type described by the -///! struct to convert the file contents accordingly. -const std = @import("std"); -const vaxis = @import("vaxis"); - -/// Markdown tronsformation to convert a markdown file as a `std.ArrayList(vaxis.cell)` -pub const Markdown = struct { - const zmd = @import("zmd"); - - const digits = "0123456789"; - - pub fn toBuffer( - input: []const u8, - allocator: std.mem.Allocator, - array: *std.ArrayList(vaxis.Cell), - ) void { - var z = zmd.Zmd.init(allocator); - defer z.deinit(); - - z.parse(input) catch @panic("failed to parse markdown contents"); - convert( - z.nodes.items[0], - allocator, - input, - array, - .{}, - null, - ) catch @panic("failed to transform parsed markdown to cell array"); - } - - fn convert( - node: *zmd.Node, - allocator: std.mem.Allocator, - input: []const u8, - array: *std.ArrayList(vaxis.Cell), - sty: vaxis.Cell.Style, - start: ?usize, - ) !void { - var next_start: ?usize = start; - var style = sty; - - // determine general styling changes - switch (node.token.element.type) { - .bold => { - style.bold = true; - next_start = node.token.start; - style.fg = .{ .index = 5 }; - }, - .italic => { - style.italic = true; - next_start = node.token.start; - style.fg = .{ .index = 2 }; - }, - .block => { - style.fg = .{ .index = 252 }; - style.dim = true; - next_start = node.token.start; - }, - .code => { - next_start = node.token.start; - style.fg = .{ .index = 6 }; - }, - .href => { - next_start = node.token.start; - style.fg = .{ .index = 8 }; - }, - .link => { - style.fg = .{ .index = 3 }; - style.ul_style = .single; - }, - .list_item => { - style.fg = .{ .index = 1 }; - }, - else => {}, - } - - // determine content that needs to be displayed - const content = value: { - switch (node.token.element.type) { - // NOTE: do not support ordered lists? (as it only accepts `1.` and not `2.`, etc.) - .text, .list_item => break :value input[node.token.start..node.token.end], - .link => break :value input[node.token.start + 1 .. node.token.start + 1 + node.title.?.len], - .code_close => { - if (next_start) |s| { - next_start = null; - break :value input[s + 1 .. node.token.end - 1]; - } - break :value ""; - }, - .bold_close, .italic_close, .block_close, .title_close, .href_close => { - if (next_start) |s| { - next_start = null; - break :value input[s..node.token.end]; - } - break :value ""; - }, - else => { - break :value ""; - }, - } - }; - - // display content - switch (node.token.element.type) { - .linebreak, .paragraph => { - try array.append(.{ - .char = .{ .grapheme = "\n" }, - .style = style, - }); - style.ul_style = .off; - }, - .h1 => { - style.ul_style = .single; - try array.append(.{ - .char = .{ .grapheme = "#" }, - .style = style, - }); - }, - .h2 => { - style.ul_style = .single; - for (0..2) |_| { - try array.append(.{ - .char = .{ .grapheme = "#" }, - .style = style, - }); - } - }, - .h3 => { - style.ul_style = .single; - for (0..3) |_| { - try array.append(.{ - .char = .{ .grapheme = "#" }, - .style = style, - }); - } - }, - .h4 => { - style.ul_style = .single; - for (0..4) |_| { - try array.append(.{ - .char = .{ .grapheme = "#" }, - .style = style, - }); - } - }, - .h5 => { - style.ul_style = .single; - for (0..5) |_| { - try array.append(.{ - .char = .{ .grapheme = "#" }, - .style = style, - }); - } - }, - .h6 => { - style.ul_style = .single; - for (0..6) |_| { - try array.append(.{ - .char = .{ .grapheme = "#" }, - .style = style, - }); - } - }, - .link => { - const uri = input[node.token.start + 1 + node.title.?.len + 2 .. node.token.start + 1 + node.title.?.len + 2 + node.href.?.len]; - for (content, 0..) |_, i| { - try array.append(.{ - .link = .{ .uri = uri }, - .char = .{ .grapheme = content[i .. i + 1] }, - .style = style, - }); - } - }, - .block_close => { - // generate a `block` i.e. - // 01 | ... - // 02 | ... - // 03 | ... - // ... - // 10 | ... - try array.append(.{ - .char = .{ .grapheme = "\n" }, - }); - var rows: usize = 0; - var c: usize = 0; - // TODO: would be cool to not have to re-iterate over the contents - for (content, 0..) |char, i| { - if (char == '\n') { - // NOTE: start after the ``` - if (c == 0) { - c = i + 1; - } - rows += 1; - } - } - rows = rows -| 1; - const pad = vaxis.widgets.LineNumbers.numDigits(rows); - for (1..rows + 1) |r| { - try array.append(.{ - .char = .{ .grapheme = " " }, - .style = style, - }); - try array.append(.{ - .char = .{ .grapheme = " " }, - .style = style, - }); - for (1..pad + 1) |i| { - const digit = vaxis.widgets.LineNumbers.extractDigit(r, pad - i); - try array.append(.{ - .char = .{ .grapheme = digits[digit .. digit + 1] }, - .style = style, - }); - } - try array.append(.{ - .char = .{ .grapheme = " " }, - .style = style, - }); - try array.append(.{ - .char = .{ .grapheme = "│" }, - .style = style, - }); - try array.append(.{ - .char = .{ .grapheme = " " }, - .style = style, - }); - for (c..content.len) |c_i| { - if (r == rows and content[c_i] == '\n') { - break; - } - try array.append(.{ - .char = .{ .grapheme = content[c_i .. c_i + 1] }, - .style = style, - }); - if (content[c_i] == '\n') { - c = c_i + 1; - break; - } - } - } - }, - .list_item => { - try array.append(.{ - .char = .{ .grapheme = "\n" }, - }); - for (content, 0..) |_, i| { - try array.append(.{ - .char = .{ .grapheme = content[i .. i + 1] }, - .style = style, - }); - } - }, - else => { - for (content, 0..) |_, i| { - try array.append(.{ - .char = .{ .grapheme = content[i .. i + 1] }, - .style = style, - }); - } - }, - } - - // close styling after creating the corresponding cells - switch (node.token.element.type) { - .bold_close => { - style.bold = false; - style.fg = .default; - }, - .italic_close => { - style.italic = false; - style.fg = .default; - }, - .block_close => { - style.fg = .default; - }, - .code_close => { - style.fg = .default; - }, - .href_close => { - style.fg = .default; - }, - .list_item => { - style.fg = .default; - }, - else => {}, - } - - // run conversion for all childrens - for (node.children.items) |child_node| { - try convert(child_node, allocator, input, array, style, next_start); - } - } -}; - -pub const Typst = struct { - pub fn toBuffer( - input: []const u8, - allocator: std.mem.Allocator, - array: *std.ArrayList(vaxis.Cell), - ) void { - // TODO: leverage the compiler to create corresponding file? - // -> would enable functions to be executed, however this is not possible currently - // -> this would only work if I serve the corresponding pdf's (maybe I can have a download server for these?) - // NOTE: currently the typst compiler does not allow such a usage from the outside - // -> I would need to wait for html export I guess - - // TODO: use pret parsing to parse typst file contents and transform them into `vaxis.Cell`s accordingly - _ = input; - _ = allocator; - _ = array; - @panic("Typst parsing not yet implemented"); - } -};