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 zpinneropts = zpinner.Options{
.suffix = " - counting sheeps",
.chars = zpinner.chars.Earth,
.style = zpinner.Style{
.foreground = .Green,
.background = .Black,
},
};
var zpin = zpinner.New(stdout_file.any(), zpinneropts);
var zpin = zpinner.new(stdout_file.any(), .{});
zpin.set_indicator(zpinner.indicator.Enum.Moon);
zpin.set_suffix(" calculating route to the moon");
try zpin.start();
std.time.sleep(5 * std.time.ns_per_s);
try zpin.stop();
try stdout_file.print("Go straight to /dev/null\n", .{});
zpin.set_suffix(" - counting crows");
std.debug.print("All my codebases are belong to you\n", .{});
zpin.set_suffix(" counting snakes");
zpin.set_indicator(zpinner.indicator.Enum.Snake);
try zpin.start();
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();
}
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());
try stdout_file.print("work done\n", .{});
}

View file

@ -1,16 +1,17 @@
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 Style = @import("ansi.zig").Style;
pub const cursor = @import("cursor.zig");
pub const format = @import("format.zig");
const builtin = @import("builtin");
/// DefaultOptions is setting the default values
const DefaultOptions = struct {
/// delay is the speed of the indicator
delay: u64 = std.time.ns_per_s * 0.1,
/// 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: []const u8 = "",
/// suffix is the text appended to the indicator
@ -24,11 +25,23 @@ const DefaultOptions = struct {
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);
}
const Zpinner = struct {
thread: std.Thread,
writer: std.io.AnyWriter,
mutex: std.Thread.Mutex,
delay: u64,
@ -38,7 +51,8 @@ const Zpinner = struct {
const Self = @This();
pub fn init(writer: anytype, opts: Options) Zpinner {
/// Initialize the zpinner
fn init(writer: anytype, opts: Options) Zpinner {
return .{
.writer = writer,
.mutex = .{},
@ -46,75 +60,104 @@ const Zpinner = struct {
.active = false,
.hide_cursor = true,
.options = opts,
.thread = undefined,
};
}
/// Start zpinning
pub fn start(self: *Self) !void {
self.mutex.lock();
try format.updateStyle(self.writer, self.options.style, null);
if (self.active or !is_tty()) {
self.mutex.unlock();
return;
}
if (self.hide_cursor) {
if (self.hide_cursor and !IsWindowsTerminalOnWindows()) {
_ = try self.writer.print("\x1B[?25l", .{});
}
self.active = true;
self.mutex.unlock();
const thread = try std.Thread.spawn(.{}, Self.run, .{self});
thread.detach();
self.thread = try std.Thread.spawn(.{}, Self.run, .{self});
}
/// Sets the appended text to the indicator
pub fn set_suffix(self: *Self, suffix: []const u8) void {
self.options.suffix = suffix;
}
/// Sets the prepended text to the indicator
pub fn set_prefix(self: *Self, prefix: []const u8) void {
self.options.prefix = prefix;
}
/// Activate the zpinner
pub fn active(self: *Self) bool {
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 {
return std.io.getStdOut().isTty();
}
fn run(self: *Self) !void {
try format.updateStyle(self.writer, self.options.style, null);
while (true) {
// const chars = "⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏";
var splits = std.mem.split(u8, self.options.chars, " ");
var splits = std.mem.split(u8, self.options.indicator.str(), " ");
while (splits.next()) |character| {
self.mutex.lock();
if (!self.mutex.tryLock()) {
return;
}
if (!self.active) {
self.mutex.unlock();
return;
}
if (!IsWindowsTerminalOnWindows()) {
try self.erase();
}
try self.writer.print("\r{s}{s}{s}", .{ self.options.prefix, character, self.options.suffix });
self.mutex.unlock();
std.time.sleep(self.options.delay);
}
}
}
pub fn stop(self: *Self) !void {
try format.resetStyle(self.writer);
self.mutex.lock();
fn erase(self: *Self) !void {
if (builtin.os.tag == .windows and !IsWindowsTerminalOnWindows()) {
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();
if (self.active) {
self.active = false;
if (self.hide_cursor) {
if (self.hide_cursor and !IsWindowsTerminalOnWindows()) {
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));
}
test "empty options struct contains default snake charset" {
const expectedValue = chars.Snake;
const expectedValue = indicator.Snake;
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" {
const expectedValue = "";