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 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());
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 = "";
|
||||||
|
|
Loading…
Reference in a new issue