Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890const std = @import("std");
const Screen = @import("Screen.zig");const Cell = @import("Cell.zig");const Mouse = @import("Mouse.zig");const Segment = @import("Cell.zig").Segment;const unicode = @import("unicode.zig");const gw = @import("gwidth.zig");
const Window = @This();
/// absolute horizontal offset from the screenx_off: i17,/// absolute vertical offset from the screeny_off: i17,/// relative horizontal offset, from parent window. This only accumulates if it is negative so that/// we can clip the window correctlyparent_x_off: i17,/// relative vertical offset, from parent window. This only accumulates if it is negative so that/// we can clip the window correctlyparent_y_off: i17,/// width of the window. This can't be larger than the terminal screenwidth: u16,/// height of the window. This can't be larger than the terminal screenheight: u16,
screen: *Screen,
/// Creates a new window with offset relative to parent and size clamped to the/// parent's size. Windows do not retain a reference to their parent and are/// unaware of resizes.fn initChild( self: Window, x_off: i17, y_off: i17, maybe_width: ?u16, maybe_height: ?u16,) Window { const max_height = @max(self.height - y_off, 0); const max_width = @max(self.width - x_off, 0); const width: u16 = maybe_width orelse max_width; const height: u16 = maybe_height orelse max_height;
return Window{ .x_off = x_off + self.x_off, .y_off = y_off + self.y_off, .parent_x_off = @min(self.parent_x_off + x_off, 0), .parent_y_off = @min(self.parent_y_off + y_off, 0), .width = @min(width, max_width), .height = @min(height, max_height), .screen = self.screen, };}
pub const ChildOptions = struct { x_off: i17 = 0, y_off: i17 = 0, /// the width of the resulting child, including any borders width: ?u16 = null, /// the height of the resulting child, including any borders height: ?u16 = null, border: BorderOptions = .{},};
pub const BorderOptions = struct { style: Cell.Style = .{}, where: union(enum) { none, all, top, right, bottom, left, other: Locations, } = .none, glyphs: Glyphs = .single_rounded,
pub const Locations = packed struct { top: bool = false, right: bool = false, bottom: bool = false, left: bool = false, };
pub const Glyphs = union(enum) { single_rounded, single_square, /// custom border glyphs. each glyph should be one cell wide and the /// following indices apply: /// [0] = top left /// [1] = horizontal /// [2] = top right /// [3] = vertical /// [4] = bottom right /// [5] = bottom left custom: [6][]const u8, };
const single_rounded: [6][]const u8 = .{ "╭", "─", "╮", "│", "╯", "╰" }; const single_square: [6][]const u8 = .{ "┌", "─", "┐", "│", "┘", "└" };};
/// create a child windowpub fn child(self: Window, opts: ChildOptions) Window { var result = self.initChild(opts.x_off, opts.y_off, opts.width, opts.height);
const glyphs = switch (opts.border.glyphs) { .single_rounded => BorderOptions.single_rounded, .single_square => BorderOptions.single_square, .custom => |custom| custom, };
const top_left: Cell.Character = .{ .grapheme = glyphs[0], .width = 1 }; const horizontal: Cell.Character = .{ .grapheme = glyphs[1], .width = 1 }; const top_right: Cell.Character = .{ .grapheme = glyphs[2], .width = 1 }; const vertical: Cell.Character = .{ .grapheme = glyphs[3], .width = 1 }; const bottom_right: Cell.Character = .{ .grapheme = glyphs[4], .width = 1 }; const bottom_left: Cell.Character = .{ .grapheme = glyphs[5], .width = 1 }; const style = opts.border.style;
const h = result.height; const w = result.width;
const loc: BorderOptions.Locations = switch (opts.border.where) { .none => return result, .all => .{ .top = true, .bottom = true, .right = true, .left = true }, .bottom => .{ .bottom = true }, .right => .{ .right = true }, .left => .{ .left = true }, .top => .{ .top = true }, .other => |loc| loc, }; if (loc.top) { var i: u16 = 0; while (i < w) : (i += 1) { result.writeCell(i, 0, .{ .char = horizontal, .style = style }); } } if (loc.bottom) { var i: u16 = 0; while (i < w) : (i += 1) { result.writeCell(i, h -| 1, .{ .char = horizontal, .style = style }); } } if (loc.left) { var i: u16 = 0; while (i < h) : (i += 1) { result.writeCell(0, i, .{ .char = vertical, .style = style }); } } if (loc.right) { var i: u16 = 0; while (i < h) : (i += 1) { result.writeCell(w -| 1, i, .{ .char = vertical, .style = style }); } } // draw corners if (loc.top and loc.left) result.writeCell(0, 0, .{ .char = top_left, .style = style }); if (loc.top and loc.right) result.writeCell(w -| 1, 0, .{ .char = top_right, .style = style }); if (loc.bottom and loc.left) result.writeCell(0, h -| 1, .{ .char = bottom_left, .style = style }); if (loc.bottom and loc.right) result.writeCell(w -| 1, h -| 1, .{ .char = bottom_right, .style = style });
const x_off: u16 = if (loc.left) 1 else 0; const y_off: u16 = if (loc.top) 1 else 0; const h_delt: u16 = if (loc.bottom) 1 else 0; const w_delt: u16 = if (loc.right) 1 else 0; const h_ch: u16 = h -| y_off -| h_delt; const w_ch: u16 = w -| x_off -| w_delt; return result.initChild(x_off, y_off, w_ch, h_ch);}
/// writes a cell to the location in the windowpub fn writeCell(self: Window, col: u16, row: u16, cell: Cell) void { if (self.height <= row or self.width <= col or self.x_off + col < 0 or self.y_off + row < 0 or self.parent_x_off + col < 0 or self.parent_y_off + row < 0) return;
self.screen.writeCell(@intCast(col + self.x_off), @intCast(row + self.y_off), cell);}
/// reads a cell at the location in the windowpub fn readCell(self: Window, col: u16, row: u16) ?Cell { if (self.height <= row or self.width <= col or self.x_off + col < 0 or self.y_off + row < 0 or self.parent_x_off + col < 0 or self.parent_y_off + row < 0) return null; return self.screen.readCell(@intCast(col + self.x_off), @intCast(row + self.y_off));}
/// fills the window with the default cellpub fn clear(self: Window) void { self.fill(.{ .default = true });}
/// returns the width of the grapheme. This depends on the terminal capabilitiespub fn gwidth(self: Window, str: []const u8) u16 { return gw.gwidth(str, self.screen.width_method);}
/// fills the window with the provided cellpub fn fill(self: Window, cell: Cell) void { if (self.x_off + self.width < 0 or self.y_off + self.height < 0 or self.screen.width < self.x_off or self.screen.height < self.y_off) return; const first_row: usize = @intCast(@max(self.y_off, 0)); if (self.x_off == 0 and self.width == self.screen.width) { // we have a full width window, therefore contiguous memory. const start = @min(first_row * self.width, self.screen.buf.len); const end = @min(start + (@as(usize, @intCast(self.height)) * self.width), self.screen.buf.len); @memset(self.screen.buf[start..end], cell); } else { // Non-contiguous. Iterate over rows an memset var row: usize = first_row; const first_col: usize = @max(self.x_off, 0); const last_row = @min(self.height + self.y_off, self.screen.height); while (row < last_row) : (row += 1) { const start = @min(first_col + (row * self.screen.width), self.screen.buf.len); var end = @min(start + self.width, start + (self.screen.width - first_col)); end = @min(end, self.screen.buf.len); @memset(self.screen.buf[start..end], cell); } }}
/// hide the cursorpub fn hideCursor(self: Window) void { self.screen.cursor_vis = false;}
/// show the cursor at the given coordinates, 0 indexedpub fn showCursor(self: Window, col: u16, row: u16) void { if (self.x_off + col < 0 or self.y_off + row < 0 or row >= self.height or col >= self.width) return; self.screen.cursor_vis = true; self.screen.cursor.row = @intCast(row + self.y_off); self.screen.cursor.col = @intCast(col + self.x_off);}
pub fn setCursorShape(self: Window, shape: Cell.CursorShape) void { self.screen.cursor_shape = shape;}
/// Options to use when printing Segments to a windowpub const PrintOptions = struct { /// vertical offset to start printing at row_offset: u16 = 0, /// horizontal offset to start printing at col_offset: u16 = 0,
/// wrap behavior for printing wrap: enum { /// wrap at grapheme boundaries grapheme, /// wrap at word boundaries word, /// stop printing after one line none, } = .grapheme,
/// when true, print will write to the screen for rendering. When false, /// nothing is written. The return value describes the size of the wrapped /// text commit: bool = true,};
pub const PrintResult = struct { col: u16, row: u16, overflow: bool,};
/// prints segments to the window. returns true if the text overflowed with the/// given wrap strategy and size.pub fn print(self: Window, segments: []const Segment, opts: PrintOptions) PrintResult { var row = opts.row_offset; switch (opts.wrap) { .grapheme => { var col: u16 = opts.col_offset; const overflow: bool = blk: for (segments) |segment| { var iter = unicode.graphemeIterator(segment.text); while (iter.next()) |grapheme| { if (col >= self.width) { row += 1; col = 0; } if (row >= self.height) break :blk true; const s = grapheme.bytes(segment.text); if (std.mem.eql(u8, s, "\n")) { row +|= 1; col = 0; continue; } const w = self.gwidth(s); if (w == 0) continue; if (opts.commit) self.writeCell(col, row, .{ .char = .{ .grapheme = s, .width = @intCast(w), }, .style = segment.style, .link = segment.link, .wrapped = col + w >= self.width, }); col += w; } } else false; if (col >= self.width) { row += 1; col = 0; } return .{ .row = row, .col = col, .overflow = overflow, }; }, .word => { var col: u16 = opts.col_offset; var overflow: bool = false; var soft_wrapped: bool = false; outer: for (segments) |segment| { var line_iter: LineIterator = .{ .buf = segment.text }; while (line_iter.next()) |line| { defer { // We only set soft_wrapped to false if a segment actually contains a linebreak if (line_iter.has_break) { soft_wrapped = false; row += 1; col = 0; } } var iter: WhitespaceTokenizer = .{ .buf = line }; while (iter.next()) |token| { switch (token) { .whitespace => |len| { if (soft_wrapped) continue; for (0..len) |_| { if (col >= self.width) { col = 0; row += 1; break; } if (opts.commit) { self.writeCell(col, row, .{ .char = .{ .grapheme = " ", .width = 1, }, .style = segment.style, .link = segment.link, }); } col += 1; } }, .word => |word| { const width = self.gwidth(word); if (width + col > self.width and width < self.width) { row += 1; col = 0; }
var grapheme_iterator = unicode.graphemeIterator(word); while (grapheme_iterator.next()) |grapheme| { soft_wrapped = false; if (row >= self.height) { overflow = true; break :outer; } const s = grapheme.bytes(word); const w = self.gwidth(s); if (opts.commit) self.writeCell(col, row, .{ .char = .{ .grapheme = s, .width = @intCast(w), }, .style = segment.style, .link = segment.link, }); col += w; if (col >= self.width) { row += 1; col = 0; soft_wrapped = true; } } }, } } } } return .{ // remove last row counter .row = row, .col = col, .overflow = overflow, }; }, .none => { var col: u16 = opts.col_offset; const overflow: bool = blk: for (segments) |segment| { var iter = unicode.graphemeIterator(segment.text); while (iter.next()) |grapheme| { if (col >= self.width) break :blk true; const s = grapheme.bytes(segment.text); if (std.mem.eql(u8, s, "\n")) break :blk true; const w = self.gwidth(s); if (w == 0) continue; if (opts.commit) self.writeCell(col, row, .{ .char = .{ .grapheme = s, .width = @intCast(w), }, .style = segment.style, .link = segment.link, }); col +|= w; } } else false; return .{ .row = row, .col = col, .overflow = overflow, }; }, } return false;}
/// print a single segment. This is just a shortcut for print(&.{segment}, opts)pub fn printSegment(self: Window, segment: Segment, opts: PrintOptions) PrintResult { return self.print(&.{segment}, opts);}
/// scrolls the window down one row (IE inserts a blank row at the bottom of the/// screen and shifts all rows up one)pub fn scroll(self: Window, n: u16) void { if (n > self.height) return; var row: u16 = @max(self.y_off, 0); const first_col: u16 = @max(self.x_off, 0); while (row < self.height - n) : (row += 1) { const dst_start = (row * self.screen.width) + first_col; const dst_end = dst_start + self.width;
const src_start = ((row + n) * self.screen.width) + first_col; const src_end = src_start + self.width; @memcpy(self.screen.buf[dst_start..dst_end], self.screen.buf[src_start..src_end]); } const last_row = self.child(.{ .y_off = self.height - n, }); last_row.clear();}
/// returns the mouse event if the mouse event occurred within the window. If/// the mouse event occurred outside the window, null is returnedpub fn hasMouse(win: Window, mouse: ?Mouse) ?Mouse { const event = mouse orelse return null; if (event.col >= win.x_off and event.col < (win.x_off + win.width) and event.row >= win.y_off and event.row < (win.y_off + win.height)) return event else return null;}
test "Window size set" { var parent = Window{ .x_off = 0, .y_off = 0, .parent_x_off = 0, .parent_y_off = 0, .width = 20, .height = 20, .screen = undefined, };
const ch = parent.initChild(1, 1, null, null); try std.testing.expectEqual(19, ch.width); try std.testing.expectEqual(19, ch.height);}
test "Window size set too big" { var parent = Window{ .x_off = 0, .y_off = 0, .parent_x_off = 0, .parent_y_off = 0, .width = 20, .height = 20, .screen = undefined, };
const ch = parent.initChild(0, 0, 21, 21); try std.testing.expectEqual(20, ch.width); try std.testing.expectEqual(20, ch.height);}
test "Window size set too big with offset" { var parent = Window{ .x_off = 0, .y_off = 0, .parent_x_off = 0, .parent_y_off = 0, .width = 20, .height = 20, .screen = undefined, };
const ch = parent.initChild(10, 10, 21, 21); try std.testing.expectEqual(10, ch.width); try std.testing.expectEqual(10, ch.height);}
test "Window size nested offsets" { var parent = Window{ .x_off = 1, .y_off = 1, .parent_x_off = 0, .parent_y_off = 0, .width = 20, .height = 20, .screen = undefined, };
const ch = parent.initChild(10, 10, 21, 21); try std.testing.expectEqual(11, ch.x_off); try std.testing.expectEqual(11, ch.y_off);}
test "Window offsets" { var parent = Window{ .x_off = 0, .y_off = 0, .parent_x_off = 0, .parent_y_off = 0, .width = 20, .height = 20, .screen = undefined, };
const ch = parent.initChild(10, 10, 21, 21); const ch2 = ch.initChild(-4, -4, null, null); // Reading ch2 at row 0 should be null try std.testing.expect(ch2.readCell(0, 0) == null); // Should not panic us ch2.writeCell(0, 0, undefined);}
test "print: grapheme" { var screen: Screen = .{ .width_method = .unicode }; const win: Window = .{ .x_off = 0, .y_off = 0, .parent_x_off = 0, .parent_y_off = 0, .width = 4, .height = 2, .screen = &screen, }; const opts: PrintOptions = .{ .commit = false, .wrap = .grapheme, };
{ var segments = [_]Segment{ .{ .text = "a" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(1, result.col); try std.testing.expectEqual(0, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "abcd" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(0, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "abcde" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(1, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "abcdefgh" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(0, result.col); try std.testing.expectEqual(2, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "abcdefghi" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(0, result.col); try std.testing.expectEqual(2, result.row); try std.testing.expectEqual(true, result.overflow); }}
test "print: word" { var screen: Screen = .{ .width_method = .unicode, }; const win: Window = .{ .x_off = 0, .y_off = 0, .parent_x_off = 0, .parent_y_off = 0, .width = 4, .height = 2, .screen = &screen, }; const opts: PrintOptions = .{ .commit = false, .wrap = .word, };
{ var segments = [_]Segment{ .{ .text = "a" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(1, result.col); try std.testing.expectEqual(0, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = " " }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(1, result.col); try std.testing.expectEqual(0, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = " a" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(2, result.col); try std.testing.expectEqual(0, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "a b" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(3, result.col); try std.testing.expectEqual(0, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "a b c" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(1, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "hello" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(1, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "hi tim" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(3, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "hello tim" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(0, result.col); try std.testing.expectEqual(2, result.row); try std.testing.expectEqual(true, result.overflow); } { var segments = [_]Segment{ .{ .text = "hello ti" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(0, result.col); try std.testing.expectEqual(2, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "h" }, .{ .text = "e" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(2, result.col); try std.testing.expectEqual(0, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "h" }, .{ .text = "e" }, .{ .text = "l" }, .{ .text = "l" }, .{ .text = "o" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(1, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "he\n" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(0, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "he\n\n" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(0, result.col); try std.testing.expectEqual(2, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "not now" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(3, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "note now" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(3, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "note" }, .{ .text = " now" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(3, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); } { var segments = [_]Segment{ .{ .text = "note " }, .{ .text = "now" }, }; const result = win.print(&segments, opts); try std.testing.expectEqual(3, result.col); try std.testing.expectEqual(1, result.row); try std.testing.expectEqual(false, result.overflow); }}
/// Iterates a slice of bytes by linebreaks. Lines are split by '\r', '\n', or '\r\n'const LineIterator = struct { buf: []const u8, index: usize = 0, has_break: bool = true,
fn next(self: *LineIterator) ?[]const u8 { if (self.index >= self.buf.len) return null;
const start = self.index; const end = std.mem.indexOfAnyPos(u8, self.buf, self.index, "\r\n") orelse { if (start == 0) self.has_break = false; self.index = self.buf.len; return self.buf[start..]; };
self.index = end; self.consumeCR(); self.consumeLF(); return self.buf[start..end]; }
// consumes a \n byte fn consumeLF(self: *LineIterator) void { if (self.index >= self.buf.len) return; if (self.buf[self.index] == '\n') self.index += 1; }
// consumes a \r byte fn consumeCR(self: *LineIterator) void { if (self.index >= self.buf.len) return; if (self.buf[self.index] == '\r') self.index += 1; }};
/// Returns tokens of text and whitespaceconst WhitespaceTokenizer = struct { buf: []const u8, index: usize = 0,
const Token = union(enum) { // the length of whitespace. Tab = 8 whitespace: usize, word: []const u8, };
fn next(self: *WhitespaceTokenizer) ?Token { if (self.index >= self.buf.len) return null; const Mode = enum { whitespace, word, }; const first = self.buf[self.index]; const mode: Mode = if (first == ' ' or first == '\t') .whitespace else .word; switch (mode) { .whitespace => { var len: usize = 0; while (self.index < self.buf.len) : (self.index += 1) { switch (self.buf[self.index]) { ' ' => len += 1, '\t' => len += 8, else => break, } } return .{ .whitespace = len }; }, .word => { const start = self.index; while (self.index < self.buf.len) : (self.index += 1) { switch (self.buf[self.index]) { ' ', '\t' => break, else => {}, } } return .{ .word = self.buf[start..self.index] }; }, } }};
test "refAllDecls" { std.testing.refAllDecls(@This());}