cleanup of obsolete comments and dead code
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 35s
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 35s
This removes the App.Renderer.Buffered, which was not improving on the direct renderer, which has a simpler implementation and was keept for this regard.
This commit is contained in:
@@ -32,7 +32,7 @@ const log = std.log.scoped(.app);
|
|||||||
/// const zterm = @import("zterm");
|
/// const zterm = @import("zterm");
|
||||||
/// const App = zterm.App(
|
/// const App = zterm.App(
|
||||||
/// union(enum) {},
|
/// union(enum) {},
|
||||||
/// zterm.Renderer.Plain,
|
/// zterm.Renderer.Direct,
|
||||||
/// true,
|
/// true,
|
||||||
/// );
|
/// );
|
||||||
/// // later on use
|
/// // later on use
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ const isTaggedUnion = @import("../event.zig").isTaggedUnion;
|
|||||||
const Error = @import("../event.zig").Error;
|
const Error = @import("../event.zig").Error;
|
||||||
const Key = terminal.Key;
|
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 {
|
pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: type) type {
|
||||||
if (!isTaggedUnion(Event)) {
|
if (!isTaggedUnion(Event)) {
|
||||||
@@ -32,7 +32,6 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
|||||||
events: Events = undefined,
|
events: Events = undefined,
|
||||||
config: Config = undefined,
|
config: Config = undefined,
|
||||||
|
|
||||||
// TODO: marging (for all 4 directions - relative!)
|
|
||||||
const Config = struct {
|
const Config = struct {
|
||||||
margin: ?u8 = undefined,
|
margin: ?u8 = undefined,
|
||||||
left: u8 = 0,
|
left: u8 = 0,
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ pub fn Layout(comptime Event: type, comptime Element: type, comptime Renderer: t
|
|||||||
events: Events = undefined,
|
events: Events = undefined,
|
||||||
config: Config = undefined,
|
config: Config = undefined,
|
||||||
|
|
||||||
// TODO: padding (for all 4 directions?)
|
|
||||||
const Config = struct {
|
const Config = struct {
|
||||||
padding: ?u16 = undefined,
|
padding: ?u16 = undefined,
|
||||||
left: u16 = 0,
|
left: u16 = 0,
|
||||||
|
|||||||
@@ -26,9 +26,8 @@ pub fn main() !void {
|
|||||||
const allocator = gpa.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
var app: App = .{};
|
var app: App = .{};
|
||||||
var renderer = App.Renderer.init(allocator);
|
var renderer: App.Renderer = .{};
|
||||||
defer renderer.deinit();
|
// TODO: when not running fullscreen, the application needs to screen down accordingly to display the contents
|
||||||
// FIX: when not running fullscreen, the application needs to screen down accordingly to display the contents
|
|
||||||
// -> size hint how much should it use?
|
// -> size hint how much should it use?
|
||||||
|
|
||||||
// var layout = Layout.createFrom(layout: {
|
// var layout = Layout.createFrom(layout: {
|
||||||
@@ -205,6 +204,5 @@ pub fn main() !void {
|
|||||||
app.postEvent(e);
|
app.postEvent(e);
|
||||||
}
|
}
|
||||||
try layout.render(&renderer);
|
try layout.render(&renderer);
|
||||||
try renderer.flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
115
src/render.zig
115
src/render.zig
@@ -1,11 +1,8 @@
|
|||||||
//! Renderer which holds the screen to compare with the previous screen for efficient rendering.
|
//! Renderer which holds the screen to compare with the previous screen for efficient rendering.
|
||||||
//! Each renderer should at least implement these functions:
|
//! Each renderer should at least implement these functions:
|
||||||
//! - init(allocator: std.mem.Allocator) @This() {}
|
|
||||||
//! - deinit(this: *@This()) void {}
|
|
||||||
//! - resize(this: *@This(), size: Size) void {}
|
//! - resize(this: *@This(), size: Size) void {}
|
||||||
//! - clear(this: *@This(), size: Size) !void {}
|
//! - clear(this: *@This(), size: Size) !void {}
|
||||||
//! - render(this: *@This(), size: Size, contents: []u8) !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
|
//! 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
|
//! change any code of any `Layout` or `Widget`. The only change should be the
|
||||||
@@ -13,117 +10,15 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const terminal = @import("terminal.zig");
|
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 Cells = []const terminal.Cell;
|
||||||
const Position = terminal.Position;
|
const Position = terminal.Position;
|
||||||
const Size = terminal.Size;
|
const Size = terminal.Size;
|
||||||
|
|
||||||
pub fn Buffered(comptime _: bool) type {
|
pub fn Direct(comptime fullscreen: 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 {
|
|
||||||
const log = std.log.scoped(.renderer_direct);
|
const log = std.log.scoped(.renderer_direct);
|
||||||
return struct {
|
|
||||||
pub fn init(allocator: std.mem.Allocator) @This() {
|
|
||||||
_ = allocator;
|
|
||||||
_ = log;
|
_ = log;
|
||||||
return .{};
|
_ = fullscreen;
|
||||||
}
|
return struct {
|
||||||
|
|
||||||
pub fn deinit(this: *@This()) void {
|
|
||||||
this.* = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(this: *@This(), size: Size) void {
|
pub fn resize(this: *@This(), size: Size) void {
|
||||||
_ = this;
|
_ = this;
|
||||||
_ = size;
|
_ = size;
|
||||||
@@ -206,9 +101,5 @@ pub fn Direct(comptime _: bool) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush(this: @This()) !void {
|
|
||||||
_ = this;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 ```<language>
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user