refactor: make code more idiomatic

- Added standard .gitattributes file for Zig projects.
- Reworked build.zig a little, hopefully it's a bit clearer. Also, now zig build will run all steps.
- outer: while in examples was redundant since there's only one loop to break from. switch expressions don't allow breaking from them, so breaking is only for loops, i.e. while and for.
- When returning a struct instance from a function, the compiler infers the return type from function signature, so instead of return MyType{...}; , it's more idiomatic to write return .{...};.
- Logging adds a new line by default, so you don't usually need to write \n like here: log.debug("event: {}\r\n", .{event});.
This commit is contained in:
Jora Troosh 2024-02-11 21:59:33 +03:00 committed by GitHub
parent a733860a21
commit 4a463cfa3a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 115 additions and 160 deletions

2
.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
*.zig text eol=lf
*.zon text eol=lf

View file

@ -53,7 +53,7 @@ const border = vaxis.widgets.border;
const log = std.log.scoped(.main);
// Our EventType. This can contain internal events as well as Vaxis events.
// This can contain internal events as well as Vaxis events.
// Internal events can be posted into the same queue as vaxis events to allow
// for a single event loop with exhaustive switching. Booya
const Event = union(enum) {
@ -102,12 +102,12 @@ pub fn main() !void {
// The main event loop. Vaxis provides a thread safe, blocking, buffered
// queue which can serve as the primary event queue for an application
outer: while (true) {
while (true) {
// nextEvent blocks until an event is in the queue
const event = vx.nextEvent();
log.debug("event: {}\r\n", .{event});
// exhaustive switching ftw. Vaxis will send events if your EventType
// enum has the fields for those events (ie "key_press", "winsize")
log.debug("event: {}", .{event});
// exhaustive switching ftw. Vaxis will send events if your Event enum
// has the fields for those events (ie "key_press", "winsize")
switch (event) {
.key_press => |key| {
color_idx = switch (color_idx) {
@ -115,7 +115,7 @@ pub fn main() !void {
else => color_idx + 1,
};
if (key.matches('c', .{ .ctrl = true })) {
break :outer;
break;
} else if (key.matches('l', .{ .ctrl = true })) {
vx.queueRefresh();
} else {

View file

@ -3,52 +3,61 @@ const std = @import("std");
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const root_source_file = std.Build.LazyPath.relative("src/main.zig");
const vaxis = b.addModule("vaxis", .{ .root_source_file = .{ .path = "src/main.zig" } });
const ziglyph = b.dependency("ziglyph", .{
// Dependencies
const ziglyph_dep = b.dependency("ziglyph", .{
.optimize = optimize,
.target = target,
});
vaxis.addImport("ziglyph", ziglyph.module("ziglyph"));
const zigimg = b.dependency("zigimg", .{
const zigimg_dep = b.dependency("zigimg", .{
.optimize = optimize,
.target = target,
});
vaxis.addImport("zigimg", zigimg.module("zigimg"));
const exe = b.addExecutable(.{
.name = "vaxis",
.root_source_file = .{ .path = "examples/pathological.zig" },
// Module
const vaxis_mod = b.addModule("vaxis", .{ .root_source_file = root_source_file });
vaxis_mod.addImport("ziglyph", ziglyph_dep.module("ziglyph"));
vaxis_mod.addImport("zigimg", zigimg_dep.module("zigimg"));
// Examples
const example_step = b.step("example", "Run examples");
const example = b.addExecutable(.{
.name = "vaxis_pathological_example",
.root_source_file = std.Build.LazyPath.relative("examples/pathological.zig"),
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("vaxis", vaxis);
example.root_module.addImport("vaxis", vaxis_mod);
const run_cmd = b.addRunArtifact(exe);
const example_run = b.addRunArtifact(example);
example_step.dependOn(&example_run.step);
b.default_step.dependOn(example_step);
run_cmd.step.dependOn(b.getInstallStep());
// Tests
const tests_step = b.step("test", "Run tests");
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
// Creates a step for unit testing. This only builds the test executable
// but does not run it.
const lib_unit_tests = b.addTest(.{
.root_source_file = .{ .path = "src/main.zig" },
const tests = b.addTest(.{
.root_source_file = root_source_file,
.target = target,
.optimize = optimize,
});
lib_unit_tests.root_module.addImport("ziglyph", ziglyph.module("ziglyph"));
lib_unit_tests.root_module.addImport("zigimg", zigimg.module("zigimg"));
tests.root_module.addImport("ziglyph", ziglyph_dep.module("ziglyph"));
tests.root_module.addImport("zigimg", zigimg_dep.module("zigimg"));
const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);
const tests_run = b.addRunArtifact(tests);
tests_step.dependOn(&tests_run.step);
b.default_step.dependOn(tests_step);
const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);
// Lints
const lints_step = b.step("lint", "Run lints");
const lints = b.addFmt(.{
.paths = &.{ "src", "build.zig" },
.check = true,
});
lints_step.dependOn(&lints.step);
b.default_step.dependOn(lints_step);
}

View file

@ -1,36 +1,15 @@
.{
.name = "vaxis",
// This is a [Semantic Version](https://semver.org/).
// In a future version of Zig it will be used for package deduplication.
.version = "0.1.0",
// This field is optional.
// This is currently advisory only; Zig does not yet do anything
// with this value.
//.minimum_zig_version = "0.11.0",
.paths = .{""},
.dependencies = .{
.ziglyph = .{
.url = "https://codeberg.org/dude_the_builder/ziglyph/archive/ac50ab06c91d2dd632ff4573c035dafe3b374aba.tar.gz",
.url = "https://codeberg.org/dude_the_builder/ziglyph/archive/ac50ab06c9.tar.gz",
.hash = "1220e097fbfb3a15a6f3484cf507f1f10ab571d1bcf519c3b5447ca727782b7a5264",
},
.zigimg = .{
.url = "https://github.com/zigimg/zigimg/archive/f6998808f283f8d3c2ef34e8b4af423bc1786f32.tar.gz",
.hash = "12202ee5d22ade0c300e9e7eae4c1951bda3d5f236fe1a139eb3613b43e2f12a88db",
}
.url = "https://github.com/zigimg/zigimg/archive/2224f91.tar.gz",
.hash = "12207067e4892c48369415268648380859baa89c324748ae5bfda414a12868c9fc8b",
},
.paths = .{
// This makes *all* files, recursively, included in this package. It is generally
// better to explicitly list the files and directories instead, to insure that
// fetching from tarballs, file system paths, and version control all result
// in the same contents hash.
"",
// For example...
//"build.zig",
//"build.zig.zon",
//"src",
//"LICENSE",
//"README.md",
},
}

View file

@ -56,7 +56,7 @@ pub fn main() !void {
const img = imgs[n];
const dims = try img.cellSize(win);
const center = vaxis.alignment.center(win, dims.cols, dims.rows);
const center = vaxis.widgets.alignment.center(win, dims.cols, dims.rows);
const scale = false;
const z_index = 0;
img.draw(center, scale, z_index);

View file

@ -32,11 +32,11 @@ pub fn main() !void {
// The main event loop. Vaxis provides a thread safe, blocking, buffered
// queue which can serve as the primary event queue for an application
outer: while (true) {
while (true) {
// nextEvent blocks until an event is in the queue
const event = vx.nextEvent();
log.debug("event: {}\r\n", .{event});
// exhaustive switching ftw. Vaxis will send events if your EventType
log.debug("event: {}", .{event});
// exhaustive switching ftw. Vaxis will send events if your Event
// enum has the fields for those events (ie "key_press", "winsize")
switch (event) {
.key_press => |key| {
@ -45,7 +45,7 @@ pub fn main() !void {
else => color_idx + 1,
};
if (key.codepoint == 'c' and key.mods.ctrl) {
break :outer;
break;
}
},
.winsize => |ws| {
@ -87,7 +87,7 @@ pub fn main() !void {
}
}
// Our EventType. This can contain internal events as well as Vaxis events.
// Our Event. This can contain internal events as well as Vaxis events.
// Internal events can be posted into the same queue as vaxis events to allow
// for a single event loop with exhaustive switching. Booya
const Event = union(enum) {

View file

@ -19,12 +19,12 @@ pub fn main() !void {
try vx.enterAltScreen();
try vx.queryTerminal();
outer: while (true) {
while (true) {
const event = vx.nextEvent();
switch (event) {
.winsize => |ws| {
try vx.resize(alloc, ws);
break :outer;
break;
},
}
}

View file

@ -6,7 +6,7 @@ const border = vaxis.widgets.border;
const log = std.log.scoped(.main);
// Our EventType. This can contain internal events as well as Vaxis events.
// Our Event. This can contain internal events as well as Vaxis events.
// Internal events can be posted into the same queue as vaxis events to allow
// for a single event loop with exhaustive switching. Booya
const Event = union(enum) {
@ -59,11 +59,11 @@ pub fn main() !void {
// The main event loop. Vaxis provides a thread safe, blocking, buffered
// queue which can serve as the primary event queue for an application
outer: while (true) {
while (true) {
// nextEvent blocks until an event is in the queue
const event = vx.nextEvent();
log.debug("event: {}\r\n", .{event});
// exhaustive switching ftw. Vaxis will send events if your EventType
log.debug("event: {}", .{event});
// exhaustive switching ftw. Vaxis will send events if your Event
// enum has the fields for those events (ie "key_press", "winsize")
switch (event) {
.key_press => |key| {
@ -72,7 +72,7 @@ pub fn main() !void {
else => color_idx + 1,
};
if (key.matches('c', .{ .ctrl = true })) {
break :outer;
break;
} else if (key.matches('l', .{ .ctrl = true })) {
vx.queueRefresh();
} else if (key.matches('n', .{ .ctrl = true })) {

View file

@ -1,11 +1,9 @@
const Image = @import("Image.zig");
pub const Cell = struct {
char: Character = .{},
style: Style = .{},
link: Hyperlink = .{},
image: ?Image.Placement = null,
};
char: Character = .{},
style: Style = .{},
link: Hyperlink = .{},
image: ?Image.Placement = null,
/// Segment is a contiguous run of text that has a constant style
pub const Segment = struct {
@ -17,7 +15,7 @@ pub const Segment = struct {
pub const Character = struct {
grapheme: []const u8 = " ",
/// width should only be provided when the application is sure the terminal
/// will meeasure the same width. This can be ensure by using the gwidth method
/// will measure the same width. This can be ensure by using the gwidth method
/// included in libvaxis. If width is 0, libvaxis will measure the glyph at
/// render time
width: usize = 1,

View file

@ -1,7 +1,7 @@
const std = @import("std");
const assert = std.debug.assert;
const Style = @import("cell.zig").Style;
const Cell = @import("cell.zig").Cell;
const Style = @import("Cell.zig").Style;
const Cell = @import("Cell.zig");
const Shape = @import("Mouse.zig").Shape;
const log = std.log.scoped(.internal_screen);

View file

@ -1,7 +1,7 @@
const std = @import("std");
const assert = std.debug.assert;
const Cell = @import("cell.zig").Cell;
const Cell = @import("Cell.zig");
const Shape = @import("Mouse.zig").Shape;
const Image = @import("Image.zig");
const Winsize = @import("Tty.zig").Winsize;

View file

@ -1,11 +1,8 @@
const std = @import("std");
const builtin = @import("builtin");
const os = std.os;
const vaxis = @import("main.zig");
const Vaxis = vaxis.Vaxis;
const Event = @import("event.zig").Event;
const Vaxis = @import("vaxis.zig").Vaxis;
const Parser = @import("Parser.zig");
const Key = vaxis.Key;
const GraphemeCache = @import("GraphemeCache.zig");
const log = std.log.scoped(.tty);
@ -68,8 +65,8 @@ pub fn stop(self: *Tty) void {
/// read input from the tty
pub fn run(
self: *Tty,
comptime EventType: type,
vx: *Vaxis(EventType),
comptime Event: type,
vx: *Vaxis(Event),
) !void {
// create a pipe so we can signal to exit the run loop
const pipe = try os.pipe();
@ -78,7 +75,7 @@ pub fn run(
// get our initial winsize
const winsize = try getWinsize(self.fd);
if (@hasField(EventType, "winsize")) {
if (@hasField(Event, "winsize")) {
vx.postEvent(.{ .winsize = winsize });
}
@ -91,10 +88,10 @@ pub fn run(
const WinchHandler = struct {
const Self = @This();
var vx_winch: *Vaxis(EventType) = undefined;
var vx_winch: *Vaxis(Event) = undefined;
var fd: os.fd_t = undefined;
fn init(vx_arg: *Vaxis(EventType), fd_arg: os.fd_t) !void {
fn init(vx_arg: *Vaxis(Event), fd_arg: os.fd_t) !void {
vx_winch = vx_arg;
fd = fd_arg;
var act = os.Sigaction{
@ -114,7 +111,7 @@ pub fn run(
const ws = getWinsize(fd) catch {
return;
};
if (@hasField(EventType, "winsize")) {
if (@hasField(Event, "winsize")) {
vx_winch.postEvent(.{ .winsize = ws });
}
}
@ -156,7 +153,7 @@ pub fn run(
const event = result.event orelse continue;
switch (event) {
.key_press => |key| {
if (@hasField(EventType, "key_press")) {
if (@hasField(Event, "key_press")) {
// HACK: yuck. there has to be a better way
var mut_key = key;
if (key.text) |text| {
@ -166,27 +163,27 @@ pub fn run(
}
},
.mouse => |mouse| {
if (@hasField(EventType, "mouse")) {
if (@hasField(Event, "mouse")) {
vx.postEvent(.{ .mouse = mouse });
}
},
.focus_in => {
if (@hasField(EventType, "focus_in")) {
if (@hasField(Event, "focus_in")) {
vx.postEvent(.focus_in);
}
},
.focus_out => {
if (@hasField(EventType, "focus_out")) {
if (@hasField(Event, "focus_out")) {
vx.postEvent(.focus_out);
}
},
.paste_start => {
if (@hasField(EventType, "paste_start")) {
if (@hasField(Event, "paste_start")) {
vx.postEvent(.paste_start);
}
},
.paste_end => {
if (@hasField(EventType, "paste_end")) {
if (@hasField(Event, "paste_end")) {
vx.postEvent(.paste_end);
}
},

View file

@ -4,8 +4,8 @@ const WordIterator = ziglyph.WordIterator;
const GraphemeIterator = ziglyph.GraphemeIterator;
const Screen = @import("Screen.zig");
const Cell = @import("cell.zig").Cell;
const Segment = @import("cell.zig").Segment;
const Cell = @import("Cell.zig");
const Segment = @import("Cell.zig").Segment;
const gw = @import("gwidth.zig");
const log = std.log.scoped(.window);
@ -118,7 +118,7 @@ pub fn wrap(self: Window, segments: []Segment) !void {
var word_iter = try WordIterator.init(segment.text);
while (word_iter.next()) |word| {
// break lines when we need
if (isLineBreak(word.bytes)) {
if (word.bytes[0] == '\r' or word.bytes[0] == '\n') {
row += 1;
col = 0;
wrapped = false;
@ -158,18 +158,6 @@ pub fn wrap(self: Window, segments: []Segment) !void {
}
}
fn isLineBreak(str: []const u8) bool {
if (std.mem.eql(u8, str, "\r\n")) {
return true;
} else if (std.mem.eql(u8, str, "\r")) {
return true;
} else if (std.mem.eql(u8, str, "\n")) {
return true;
} else {
return false;
}
}
test "Window size set" {
var parent = Window{
.x_off = 0,

View file

@ -1,40 +1,21 @@
const std = @import("std");
pub const Vaxis = @import("vaxis.zig").Vaxis;
pub const Options = @import("Options.zig");
const cell = @import("cell.zig");
pub const Cell = cell.Cell;
pub const Style = cell.Style;
pub const Segment = cell.Segment;
pub const Color = cell.Color;
pub const Key = @import("Key.zig");
pub const Cell = @import("Cell.zig");
pub const Image = @import("Image.zig");
pub const Mouse = @import("Mouse.zig");
pub const Winsize = @import("Tty.zig").Winsize;
pub const widgets = @import("widgets/main.zig");
pub const alignment = widgets.alignment;
pub const border = widgets.border;
pub const Image = @import("Image.zig");
pub const widgets = @import("widgets.zig");
/// Initialize a Vaxis application.
pub fn init(comptime EventType: type, opts: Options) !Vaxis(EventType) {
return Vaxis(EventType).init(opts);
pub fn init(comptime Event: type, opts: Options) !Vaxis(Event) {
return Vaxis(Event).init(opts);
}
test {
_ = @import("GraphemeCache.zig");
_ = @import("Key.zig");
_ = @import("Mouse.zig");
_ = @import("Options.zig");
_ = @import("Parser.zig");
_ = @import("Screen.zig");
_ = @import("Tty.zig");
_ = @import("Window.zig");
_ = @import("cell.zig");
_ = @import("ctlseqs.zig");
_ = @import("event.zig");
_ = @import("gwidth.zig");
_ = @import("queue.zig");
_ = @import("vaxis.zig");
std.testing.refAllDecls(@This());
}

View file

@ -11,8 +11,8 @@ const Screen = @import("Screen.zig");
const InternalScreen = @import("InternalScreen.zig");
const Window = @import("Window.zig");
const Options = @import("Options.zig");
const Style = @import("cell.zig").Style;
const Hyperlink = @import("cell.zig").Hyperlink;
const Style = @import("Cell.zig").Style;
const Hyperlink = @import("Cell.zig").Hyperlink;
const gwidth = @import("gwidth.zig");
const Shape = @import("Mouse.zig").Shape;
const Image = @import("Image.zig");
@ -34,7 +34,7 @@ pub fn Vaxis(comptime T: type) type {
const log = std.log.scoped(.vaxis);
pub const EventType = T;
pub const Event = T;
pub const Capabilities = struct {
kitty_keyboard: bool = false,
@ -83,7 +83,7 @@ pub fn Vaxis(comptime T: type) type {
/// Initialize Vaxis with runtime options
pub fn init(_: Options) !Self {
return Self{
return .{
.queue = .{},
.tty = null,
.screen = .{},
@ -151,7 +151,7 @@ pub fn Vaxis(comptime T: type) type {
self.queue.push(event);
}
/// resize allocates a slice of cellsequal to the number of cells
/// resize allocates a slice of cells equal to the number of cells
/// required to display the screen (ie width x height). Any previous screen is
/// freed when resizing
pub fn resize(self: *Self, alloc: std.mem.Allocator, winsize: Winsize) !void {
@ -169,7 +169,7 @@ pub fn Vaxis(comptime T: type) type {
/// returns a Window comprising of the entire terminal screen
pub fn window(self: *Self) Window {
return Window{
return .{
.x_off = 0,
.y_off = 0,
.width = self.screen.width,
@ -207,7 +207,7 @@ pub fn Vaxis(comptime T: type) type {
if (std.mem.eql(u8, colorterm, "truecolor") or
std.mem.eql(u8, colorterm, "24bit"))
{
if (@hasField(EventType, "cap_rgb")) {
if (@hasField(Event, "cap_rgb")) {
self.postEvent(.cap_rgb);
}
}
@ -348,7 +348,7 @@ pub fn Vaxis(comptime T: type) type {
}
}
// something is different, so let's loop throuugh everything and
// something is different, so let's loop through everything and
// find out what
// foreground
@ -642,7 +642,7 @@ pub fn Vaxis(comptime T: type) type {
}
}
try tty.buffered_writer.flush();
return Image{
return .{
.id = id,
.width = img.width,
.height = img.height,

3
src/widgets.zig Normal file
View file

@ -0,0 +1,3 @@
pub const border = @import("widgets/border.zig");
pub const alignment = @import("widgets/alignment.zig");
pub const TextInput = @import("widgets/TextInput.zig");

View file

@ -1,6 +1,6 @@
const std = @import("std");
const Cell = @import("../cell.zig").Cell;
const Key = @import("../Key.zig");
const Cell = @import("../Cell.zig");
const Window = @import("../Window.zig");
const GraphemeIterator = @import("ziglyph").GraphemeIterator;

View file

@ -1,7 +1,8 @@
const Cell = @import("../Cell.zig");
const Window = @import("../Window.zig");
const cell = @import("../cell.zig");
const Character = cell.Character;
const Style = cell.Style;
const Style = Cell.Style;
const Character = Cell.Character;
const horizontal = Character{ .grapheme = "", .width = 1 };
const vertical = Character{ .grapheme = "", .width = 1 };

View file

@ -1,3 +0,0 @@
pub const TextInput = @import("TextInput.zig");
pub const border = @import("border.zig");
pub const alignment = @import("align.zig");