feat: Support windows

This commit is contained in:
Kalle Carlbark 2024-05-29 23:13:25 +02:00
parent 7f47321066
commit 9f09cc1b6f
No known key found for this signature in database
4 changed files with 94 additions and 43 deletions

View file

@ -1,4 +0,0 @@
pub const Moon = "🌑 🌒 🌓 🌔 🌕 🌖 🌗 🌘";
pub const Snake = "⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏";
pub const SnakeLoad = "⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷";
pub const Earth = "🌍 🌎 🌏";

22
src/indicator.zig Normal file
View file

@ -0,0 +1,22 @@
pub const Moon = "🌑 🌒 🌓 🌔 🌕 🌖 🌗 🌘";
pub const Snake = "⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏";
pub const SnakeLoad = "⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷";
pub const Earth = "🌍 🌎 🌏";
pub const Enum = enum {
Moon,
Snake,
SnakeLoad,
Earth,
pub const EnumTable = [@typeInfo(Enum).Enum.fields.len][:0]const u8{
Moon,
Snake,
SnakeLoad,
Earth,
};
pub fn str(self: Enum) [:0]const u8 {
return EnumTable[@intFromEnum(self)];
}
};

View file

@ -6,31 +6,21 @@ pub fn main() !void {
const stdout_file = std.io.getStdOut().writer(); const stdout_file = std.io.getStdOut().writer();
const zpinneropts = zpinner.Options{ var zpin = zpinner.new(stdout_file.any(), .{});
.suffix = " - counting sheeps", zpin.set_indicator(zpinner.indicator.Enum.Moon);
.chars = zpinner.chars.Earth, zpin.set_suffix(" calculating route to the moon");
.style = zpinner.Style{
.foreground = .Green,
.background = .Black,
},
};
var zpin = zpinner.New(stdout_file.any(), zpinneropts);
try zpin.start(); try zpin.start();
std.time.sleep(5 * std.time.ns_per_s); std.time.sleep(5 * std.time.ns_per_s);
try zpin.stop(); try zpin.stop();
try stdout_file.print("Go straight to /dev/null\n", .{});
zpin.set_suffix(" - counting crows"); zpin.set_suffix(" counting snakes");
zpin.set_indicator(zpinner.indicator.Enum.Snake);
std.debug.print("All my codebases are belong to you\n", .{});
try zpin.start(); try zpin.start();
std.time.sleep(5 * std.time.ns_per_s); std.time.sleep(5 * std.time.ns_per_s);
zpin.set_indicator(zpinner.indicator.Enum.Moon);
zpin.set_suffix(" calculating route back to earth");
std.time.sleep(5 * std.time.ns_per_s);
try zpin.stop(); try zpin.stop();
} try stdout_file.print("work done\n", .{});
test "simple test" {
var list = std.ArrayList(i32).init(std.testing.allocator);
defer list.deinit(); // try commenting this out and see if zig detects the memory leak!
try list.append(42);
try std.testing.expectEqual(@as(i32, 42), list.pop());
} }

View file

@ -1,16 +1,17 @@
pub const std = @import("std"); pub const std = @import("std");
pub const chars = @import("characters.zig"); pub const indicator = @import("indicator.zig");
pub const Color = @import("ansi.zig").Color; pub const Color = @import("ansi.zig").Color;
pub const Style = @import("ansi.zig").Style; pub const Style = @import("ansi.zig").Style;
pub const cursor = @import("cursor.zig"); pub const cursor = @import("cursor.zig");
pub const format = @import("format.zig"); pub const format = @import("format.zig");
const builtin = @import("builtin");
/// DefaultOptions is setting the default values /// DefaultOptions is setting the default values
const DefaultOptions = struct { const DefaultOptions = struct {
/// delay is the speed of the indicator /// delay is the speed of the indicator
delay: u64 = std.time.ns_per_s * 0.1, delay: u64 = std.time.ns_per_s * 0.1,
/// chars to loop through see characters.zig /// chars to loop through see characters.zig
chars: []const u8 = chars.Snake, indicator: indicator.Enum = indicator.Enum.Snake,
/// prefix is the text preppended to the indicator /// prefix is the text preppended to the indicator
prefix: []const u8 = "", prefix: []const u8 = "",
/// suffix is the text appended to the indicator /// suffix is the text appended to the indicator
@ -24,11 +25,23 @@ const DefaultOptions = struct {
pub const Options = DefaultOptions; pub const Options = DefaultOptions;
pub fn New(writer: anytype, opts: Options) Zpinner { fn IsWindowsTerminalOnWindows() bool {
const wt_session = std.posix.getenv("WT_SESSION") orelse "";
if (wt_session.len > 0 and builtin.os.tag == .windows) {
return true;
}
return false;
}
/// Create new zpinner
/// Needs a writer().any()
pub fn new(writer: anytype, opts: Options) Zpinner {
return Zpinner.init(writer, opts); return Zpinner.init(writer, opts);
} }
const Zpinner = struct { const Zpinner = struct {
thread: std.Thread,
writer: std.io.AnyWriter, writer: std.io.AnyWriter,
mutex: std.Thread.Mutex, mutex: std.Thread.Mutex,
delay: u64, delay: u64,
@ -38,7 +51,8 @@ const Zpinner = struct {
const Self = @This(); const Self = @This();
pub fn init(writer: anytype, opts: Options) Zpinner { /// Initialize the zpinner
fn init(writer: anytype, opts: Options) Zpinner {
return .{ return .{
.writer = writer, .writer = writer,
.mutex = .{}, .mutex = .{},
@ -46,75 +60,104 @@ const Zpinner = struct {
.active = false, .active = false,
.hide_cursor = true, .hide_cursor = true,
.options = opts, .options = opts,
.thread = undefined,
}; };
} }
/// Start zpinning
pub fn start(self: *Self) !void { pub fn start(self: *Self) !void {
self.mutex.lock(); self.mutex.lock();
try format.updateStyle(self.writer, self.options.style, null);
if (self.active or !is_tty()) { if (self.active or !is_tty()) {
self.mutex.unlock(); self.mutex.unlock();
return; return;
} }
if (self.hide_cursor) { if (self.hide_cursor and !IsWindowsTerminalOnWindows()) {
_ = try self.writer.print("\x1B[?25l", .{}); _ = try self.writer.print("\x1B[?25l", .{});
} }
self.active = true; self.active = true;
self.mutex.unlock(); self.mutex.unlock();
const thread = try std.Thread.spawn(.{}, Self.run, .{self}); self.thread = try std.Thread.spawn(.{}, Self.run, .{self});
thread.detach();
} }
/// Sets the appended text to the indicator
pub fn set_suffix(self: *Self, suffix: []const u8) void { pub fn set_suffix(self: *Self, suffix: []const u8) void {
self.options.suffix = suffix; self.options.suffix = suffix;
} }
/// Sets the prepended text to the indicator
pub fn set_prefix(self: *Self, prefix: []const u8) void { pub fn set_prefix(self: *Self, prefix: []const u8) void {
self.options.prefix = prefix; self.options.prefix = prefix;
} }
/// Activate the zpinner
pub fn active(self: *Self) bool { pub fn active(self: *Self) bool {
return self.active; return self.active;
} }
/// Set indicator
pub fn set_indicator(self: *Self, indicator_enum: indicator.Enum) void {
self.options.indicator = indicator_enum;
}
fn is_tty() bool { fn is_tty() bool {
return std.io.getStdOut().isTty(); return std.io.getStdOut().isTty();
} }
fn run(self: *Self) !void { fn run(self: *Self) !void {
try format.updateStyle(self.writer, self.options.style, null);
while (true) { while (true) {
// const chars = "⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏"; var splits = std.mem.split(u8, self.options.indicator.str(), " ");
var splits = std.mem.split(u8, self.options.chars, " ");
while (splits.next()) |character| { while (splits.next()) |character| {
self.mutex.lock(); if (!self.mutex.tryLock()) {
return;
}
if (!self.active) { if (!self.active) {
self.mutex.unlock(); self.mutex.unlock();
return; return;
} }
if (!IsWindowsTerminalOnWindows()) {
try self.erase();
}
try self.writer.print("\r{s}{s}{s}", .{ self.options.prefix, character, self.options.suffix }); try self.writer.print("\r{s}{s}{s}", .{ self.options.prefix, character, self.options.suffix });
self.mutex.unlock(); self.mutex.unlock();
std.time.sleep(self.options.delay); std.time.sleep(self.options.delay);
} }
} }
} }
pub fn stop(self: *Self) !void { fn erase(self: *Self) !void {
try format.resetStyle(self.writer); if (builtin.os.tag == .windows and !IsWindowsTerminalOnWindows()) {
self.mutex.lock(); const prefix_len = try std.unicode.utf8CountCodepoints(self.options.prefix);
const suffix_len = try std.unicode.utf8CountCodepoints(self.options.suffix);
try self.writer.print("\r", .{});
try self.writer.writeByteNTimes(' ', prefix_len + suffix_len + 1);
try self.writer.print("\r", .{});
return;
}
try self.writer.print("\r\x1B[K", .{});
}
/// Stops the zpinner
pub fn stop(self: *Self) !void {
self.mutex.lock();
defer self.mutex.unlock(); defer self.mutex.unlock();
if (self.active) { if (self.active) {
self.active = false; self.active = false;
if (self.hide_cursor) { if (self.hide_cursor and !IsWindowsTerminalOnWindows()) {
try cursor.showCursor(self.writer); try cursor.showCursor(self.writer);
// try try self.writer.print("\x1B[?25h", .{});
} }
try self.writer.print("\r", .{}); try self.erase();
self.thread.join();
} }
} }
}; };
@ -127,11 +170,11 @@ test "empty options struct contains default delay value" {
try std.testing.expectEqual(@as(u64, defaultOptions.delay), @as(u64, expectedValue)); try std.testing.expectEqual(@as(u64, defaultOptions.delay), @as(u64, expectedValue));
} }
test "empty options struct contains default snake charset" { test "empty options struct contains default snake charset" {
const expectedValue = chars.Snake; const expectedValue = indicator.Snake;
const opts = Options{}; const opts = Options{};
try std.testing.expectEqual(opts.chars, expectedValue); try std.testing.expectEqual(opts.indicator.str(), expectedValue);
} }
test "empty options struct contains default empty prefix" { test "empty options struct contains default empty prefix" {
const expectedValue = ""; const expectedValue = "";