Hey everyone, hope this is the right place for this question.
I was experimenting a bit with unions, trying to create a small neat interface for generating ANSI color escape codes at comptime. I settled on trying a combination of a color enum, a code union and a generator function which will generate the final output given a slice of codes:
Example usage:
print(ansi(&.{ .bold, .bg(.Green), .fg256(231) }) ++ " DEBUG " ++ ansi(&.{.reset}));
print(ansi(&.{ .blink, .bgRGB(24, 87, 69), .fg(.Red) }) ++ " TEST " ++ ansi(&.{.reset}));
Full Code
// Codes ripped from ANSI cheatsheet: https://gist.github.com/ConnerWill/d4b6c776b509add763e17f9f113fd25b
//
//
//! Small utility providing a small interface to generate ANSI color codes.
//!
//! Example:
//! const text = comptime ansi(&.{ .bold, .bg(.Green), .fg256(231) }) ++ "I will be styled!" ++ ansi(&.{.reset});
//! const text = comptime ansi(&.{ .blink, .fgRGB(23, 180, 156) }) ++ "I will be styled!" ++ ansi(&.{.reset});
//!
const std = @import("std");
// TODO: Impl "Bright" colors?
pub const AnsiColor = enum(u8) {
Black = 0,
Red = 1,
Green = 2,
Yellow = 3,
Blue = 4,
Magenta = 5,
Cyan = 6,
White = 7,
};
pub const AnsiCode = union(enum) {
// NOTE: Don't use these directly, the fg, bg, fg256, bg256, fgRGB, bgRGB functions
// provide a more compact interface instead!
_fg: AnsiColor,
_bg: AnsiColor,
_fg_256: u8,
_bg_256: u8,
_fg_rgb: @Vector(3, u8),
_bg_rgb: @Vector(3, u8),
reset,
bold,
dim,
italic,
underline,
blink,
reverse,
hidden,
strike,
resetBoldDim, // Resets both as per spec
resetItalic,
resetUnderline,
resetBlink,
resetReverse,
resetHidden,
resetStrike,
pub fn fg(color: AnsiColor) AnsiCode {
return .{ ._fg = color };
}
pub fn bg(color: AnsiColor) AnsiCode {
return .{ ._bg = color };
}
pub fn fg256(color: u8) AnsiCode {
return .{ ._fg_256 = color };
}
pub fn bg256(color: u8) AnsiCode {
return .{ ._bg_256 = color };
}
pub fn fgRGB(r: u8, g: u8, b: u8) AnsiCode {
return .{ ._fg_rgb = .{ r, g, b } };
}
pub fn bgRGB(r: u8, g: u8, b: u8) AnsiCode {
return .{ ._bg_rgb = .{ r, g, b } };
}
/// Returns the individual ANSI code number string.
/// NOTE: This is only intended for internal use to build a full formating string!
fn toCodePointString(self: AnsiCode) []const u8 {
return switch (self) {
.reset => "0",
.bold => "1",
.dim => "2",
.italic => "3",
.underline => "4",
.blink => "5",
.reverse => "7",
.hidden => "8",
.strike => "9",
.resetBoldDim => "22",
.resetItalic => "23",
.resetUnderline => "24",
.resetBlink => "25",
.resetReverse => "27",
.resetHidden => "28",
.resetStrike => "29",
._fg => |col| std.fmt.comptimePrint("3{d}", .{@intFromEnum(col)}),
._bg => |col| std.fmt.comptimePrint("4{d}", .{@intFromEnum(col)}),
._fg_256 => |col| std.fmt.comptimePrint("38;5;{d}", .{col}),
._bg_256 => |col| std.fmt.comptimePrint("48;5;{d}", .{col}),
._fg_rgb => |col| std.fmt.comptimePrint("38;2;{d};{d};{d}", .{ col[0], col[1], col[2] }),
._bg_rgb => |col| std.fmt.comptimePrint("48;2;{d};{d};{d}", .{ col[0], col[1], col[2] }),
};
}
/// Returns the full ANSI formatting string.
pub fn toString(self: AnsiCode) []const u8 {
return "\x1b[" ++ self.toCodePointString() ++ "m";
}
};
pub fn ansi(comptime codes: []const AnsiCode) []const u8 {
comptime {
var buf: []const u8 = "\x1b[";
for (codes, 0..) |code, i| {
buf = buf ++ code.toCodePointString();
if (i + 1 < codes.len) buf = buf ++ ";";
}
buf = buf ++ "m";
return buf;
}
}
When I use the ansi though:
- I don’t get any LSP hints for the
[]const AnsiCodeargument. It compiles and works just fine, but I never get any hints but rather just a generic list:
- I also tried it with enum functions and there I did get the expected hints for the enum values themselves, but also no hints for any functions on the enum:
https://imgur.com/a/1tNjymC (sry, not allowed to embed > 1 image)
I didn’t find any info on this… so i am curious, is this some issue with my LSP setup (nvim maybe?) or an issue with ZLS itself?
There is this old issue Enum completions are broken (missing functions & declarations) · Issue #1266 · zigtools/zls · GitHub talking about missing enum functions, but it was closed some time ago…
I am not sure if this is a new issue, or if it is related to the fact that I am using them inside the []const AnsiCode slice.
