feat: Support windows
This commit is contained in:
parent
7f47321066
commit
9f09cc1b6f
4 changed files with 94 additions and 43 deletions
|
@ -1,4 +0,0 @@
|
|||
pub const Moon = "🌑 🌒 🌓 🌔 🌕 🌖 🌗 🌘";
|
||||
pub const Snake = "⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏";
|
||||
pub const SnakeLoad = "⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷";
|
||||
pub const Earth = "🌍 🌎 🌏";
|
22
src/indicator.zig
Normal file
22
src/indicator.zig
Normal 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)];
|
||||
}
|
||||
};
|
30
src/main.zig
30
src/main.zig
|
@ -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", .{});
|
||||
}
|
||||
|
|
|
@ -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 = "";
|
||||
|
|
Loading…
Reference in a new issue