Author:ptrace Comitter:ptrace Date:2025-11-08 22:21:09 UTC

1

diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..94e910b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ # Jai .build bin *.codex diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..103d568 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ MIT License Copyright (c) 2025 dev@ptrace.dev Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..04fe40c --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ # Terminal Colors for Jai This is a small declarative library which allows you to create colorful terminal messages. ## Usage Put the directory `termcolors` in your projects `modules` directory, or link it while building.   ### Example ```plain #import "termcolors"; main :: () {     context.allocator = temp;                         font style     background color                         v              v     log(paint("FooBar", .BOLD, .BLACK, .WHITE));               ^                ^               text             foreground color                                                               background color                                                               v     log(paint_ex("FooBar", .[.BOLD, .UNDERLINE, .ITALIC], 84, 124));                  ^           ^                            ^                  text        multiple font styles         foreground 256bit color } ``` ### More Information There are several APIs and overloads, which are ranging from static/more declarative, to  flexible/fully customizable.   Please consult `termcolors/lib.jai` for a more detailed documentation.   You can also view `test/test.jai` for more examples. If you want to run those examples,  execute them with `jai build.jai - run silent`.   ## Credits - https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences diff --git a/build.jai b/build.jai new file mode 100644 index 0000000..2749bbb --- /dev/null +++ b/build.jai @@ -0,0 +1,115 @@ #import "Basic"; #import "Compiler"; #import "File"; #import "Autorun"; build :: () {     args := get_build_options().compile_time_command_line;     // args     args_help               := array_find(args, "help");     args_compiler_silent    := array_find(args, "silent");     args_program_run        := array_find(args, "run");     args_build_release      := array_find(args, "release");     // -----------------------------------------     w := compiler_create_workspace();     if !w {         log("Workspace creation failed.");         return;     }     set_build_options_dc(.{ do_output = false });     if args_help {         args_help_print();         return;     }     print("The workspace w is %\n", w);     make_directory_if_it_does_not_exist("bin");     target_options := get_build_options(w);     target_options.output_executable_name = "program";     target_options.output_path = "bin";     import_path: [..] string;     array_add(*import_path, ..target_options.import_path);     array_add(*import_path, ".");     target_options.import_path = import_path;     if args_compiler_silent  target_options.text_output_flags = 0;     if args_build_release {         build_release(w, *target_options);     } else {         build_debug(w, *target_options);     }     compiler_begin_intercept(w);     add_build_file(tprint("%test/test.jai", #filepath), w);     jinit_compiler_response := jinit_compiler_intercept();     compiler_end_intercept(w);     if !jinit_compiler_response {         log("Compiler response failed.");         return;     }     if args_program_run  run_build_result(w); } // adapted from: https://github.com/Ivo-Balbaert/The_Way_to_Jai/blob/main/book/30B_Manipulating_the_build_process.md build_debug :: (w: Workspace, target_options: *Build_Options) {     log("Choosing debug options...");     target_options.backend =.X64;     set_optimization(target_options, Optimization_Type.DEBUG, true);     set_build_options(target_options.*, w); } build_release :: (w: Workspace, target_options: *Build_Options) {     log("Choosing release options...");     target_options.backend = .LLVM;     set_optimization(target_options, Optimization_Type.VERY_OPTIMIZED);     set_build_options(target_options.*, w); } args_help_print :: () {     help_message := #string _END_ Usage:  jai build.jai - [ARGS] Options:     help        Prints this help menu     silent      Disables compiler/linker statistics     run         Runs your program afterwards     release     Builds with release options. If omitted, it builds a debug build. _END_;     log(help_message); } jinit_compiler_intercept :: () -> success: bool {     while true {         message := compiler_wait_for_message();         if !message break;         if message.kind == {         case .COMPLETE;             message_complete := cast(*Message_Complete) message;             return message_complete.error_code == 0;         }     }     return false; } main :: () {} #run build(); diff --git a/docs/termcodes.md b/docs/termcodes.md new file mode 100644 index 0000000..ebb1d3f --- /dev/null +++ b/docs/termcodes.md @@ -0,0 +1,47 @@ # Terminal Codes Credits: https://stackoverflow.com/questions/4842424/list-of-ansi-color-escape-sequences Code    Effect                              Note 0       Reset / Normal                      all attributes off 1       Bold or increased intensity 2       Faint (decreased intensity)         Not widely supported. 3       Italic                              Not widely supported. Sometimes treated as inverse. 4       Underline 5       Slow Blink                          less than 150 per minute 6       Rapid Blink                         MS-DOS ANSI.SYS; 150+ per minute; not widely supported 7       [[reverse video]]                   swap foreground and background colors 8       Conceal                             Not widely supported. 9       Crossed-out                         Characters legible, but marked for deletion. Not widely supported. 10      Primary(default) font 11–19   Alternate font                      Select alternate font n-10 20      Fraktur                             hardly ever supported 21      Bold off or Double Underline        Bold off not widely supported; double underline hardly ever supported. 22      Normal color or intensity           Neither bold nor faint 23      Not italic, not Fraktur 24      Underline off                       Not singly or doubly underlined 25      Blink off 27      Inverse off 28      Reveal                              conceal off 29      Not crossed out 30–37   Set foreground color                See color table below 38      Set foreground color                Next arguments are 5;<n> or 2;<r>;<g>;<b>, see below 39      Default foreground color            implementation defined (according to standard) 40–47   Set background color                See color table below 48      Set background color                Next arguments are 5;<n> or 2;<r>;<g>;<b>, see below 49      Default background color            implementation defined (according to standard) 51      Framed 52      Encircled 53      Overlined 54      Not framed or encircled 55      Not overlined 60      ideogram underline                  hardly ever supported 61      ideogram double underline           hardly ever supported 62      ideogram overline                   hardly ever supported 63      ideogram double overline            hardly ever supported 64      ideogram stress marking             hardly ever supported 65      ideogram attributes off             reset the effects of all of 60-64 90–97   Set bright foreground color         aixterm (not in standard) 100–107 Set bright background color         aixterm (not in standard) diff --git a/termcolors/lib.jai b/termcolors/lib.jai new file mode 100644 index 0000000..92a61c4 --- /dev/null +++ b/termcolors/lib.jai @@ -0,0 +1,498 @@ /*  * MIT License  *  * Copyright (c) 2025 dev@ptrace.dev  *  * Permission is hereby granted, free of charge, to any person obtaining a copy  * of this software and associated documentation files (the "Software"), to deal  * in the Software without restriction, including without limitation the rights  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  * copies of the Software, and to permit persons to whom the Software is  * furnished to do so, subject to the following conditions:  *  * The above copyright notice and this permission notice shall be included in all  * copies or substantial portions of the Software.  *  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE  * SOFTWARE.  *  */ /*     +Version: 1.0.0     -------------------     --- [ paint() ] ---     -------------------     paint() uses the 4bit terminal color palette. The structure of the proc signature     through every API is the same:         `text, font style, foreground color, background color`     Only `text` is mandatory. Other params can be omitted since they default to `.NONE`.     [Note]: You can overload each proc for more flexibility.     This prints text without any formatting.         log(paint("Foo Bar"));     Applies a font weight, foreground and background colors         log(paint("Foo Bar", .BOLD, .BLACK, .WHITE));     Applies multiple text decorations         log(paint("Foo Bar", .[.BOLD, .UNDERLINE, .ITALIC], .BLACK, .WHITE));     ----------------------     --- [ paint_ex() ] ---     ----------------------     paint_ex() uses the 256bit color palette. There are some predefined colors you can use.         log(paint_ex("Foo Bar", .UNDERLINE, .GREEN_DARK, .ORANGE_LIGHT));     It also allows multiple font styles.         log(paint_ex("Foo Bar", .[.BOLD, .UNDERLINE, .ITALIC], .GREEN_DARK, .ORANGE_LIGHT));     If you want to have more control over the colors, you can use integers.         log(paint_ex("Foo Bar", .UNDERLINE, 84, 124));         log(paint_ex("Foo Bar", .[.BOLD, .UNDERLINE, .ITALIC], 84, 124));     You could even create a own color palette. Just create a enum with this signature:         My_Colors :: enum #specified {             NONE :: -1;             COL1 :: 84;             COL2 :: 124;         }     And use `paint_ex_custom()` like this:         log(paint_ex_custom("Foo Bar", .UNDERLINE, My_Colors.COL1, My_Colors.COL2));     The downside is, you cannot omit the fore- and background color. If you want that      feature, you have to wrap this proc in a custom proc.     -----------------------     --- [ paint_rgb() ] ---     -----------------------     If you want to use RGB values you can to it like that:         log(paint_rgb("Foo Bar", .UNDERLINE, .{ 255, 0, 0 }, .{ 0, 0, 255 }));         log(paint_rgb("Foo Bar", .[.BOLD, .UNDERLINE, .ITALIC], .{ 255, 0, 0 }, .{ 0, 0, 255 }));     Consult the enums below for more colors.     ---------------------------------------------------     --- [ Using String Literals as Terminal Codes ] ---     ---------------------------------------------------     If you need "raw" access because you want to build more complex stuff:         paint_raw();     Example:         log(paint_raw("1;3;32;45", "Foo Bar"));     --------------------------------     --- [ Notes on Performance ] ---     --------------------------------     Since those APIs providing flexibility, they have to branch a few times.     Which won't be a negative hit on most programs. But if you're developing     something hyper-fast, those APIs could be a perf hit.     To bypass this, you can just use this proc:         - paint_raw()     Which basically only fprints this string: `"\u001b[%m%\u001b[0m"`.     --------------------------------------     --- [ Notes on Memory Allocation ] ---     --------------------------------------     All APIs returning a string, which you have to manage.     Depending on your use case, you might be fine with pushing it to     the temporary storage via comma-comma:         log(pain("Foo Baar", .BOLD, .BLACK, .WHITE,, temp));     or         context.allocator = temp;         log(pain("Foo Baar"));         log(pain("Foo Baar", .BOLD, .BLACK, .WHITE)); */ #import "Basic"; #import "String"; paint :: (     str: string,     style: Text_Style = .NONE,     fg: Color_Foreground = .NONE,     bg: Color_Background = .NONE ) -> string {     return __paint(str, .[style], fg, bg, "", ""); } paint :: (     str: string,     style: []Text_Style = .[],     fg: Color_Foreground = .NONE,     bg: Color_Background = .NONE ) -> string {     return __paint(str, style, fg, bg, "", ""); } paint_ex :: (     str: string,     style: Text_Style = .NONE,     fg_color: Color_Table = .NONE,     bg_color: Color_Table = .NONE ) -> string {     return __paint(str, .[style], fg_color, bg_color,         FOREGROUND_COLOR_FROM_EXT_TABLE,         BACKGROUND_COLOR_FROM_EXT_TABLE     ); } paint_ex :: (     str: string,     style: Text_Style = .NONE,     fg_color: int = -1,     bg_color: int = -1 ) -> string {     return __paint(str, .[style], fg_color, bg_color,         FOREGROUND_COLOR_FROM_EXT_TABLE,         BACKGROUND_COLOR_FROM_EXT_TABLE     ); } paint_ex :: (     str: string,     style: []Text_Style = .[],     fg_color: int = -1,     bg_color: int = -1 ) -> string {     return __paint(str, style, fg_color, bg_color,         FOREGROUND_COLOR_FROM_EXT_TABLE,         BACKGROUND_COLOR_FROM_EXT_TABLE     ); } paint_ex :: (     str: string,     style: []Text_Style = .[],     fg_color: Color_Table = .NONE,     bg_color: Color_Table = .NONE ) -> string {     return __paint(str, style, fg_color, bg_color,         FOREGROUND_COLOR_FROM_EXT_TABLE,         BACKGROUND_COLOR_FROM_EXT_TABLE     ); } paint_ex_custom :: (     str: string,     style: Text_Style = .NONE,     fg_color: $A,     bg_color: $B ) -> string {     return __paint(str, .[style], fg_color, bg_color,         FOREGROUND_COLOR_FROM_EXT_TABLE,         BACKGROUND_COLOR_FROM_EXT_TABLE     ); } paint_rgb :: (     str: string,     style: Text_Style = .NONE,     fg_rgb: Term_Rgb,     bg_rgb: Term_Rgb ) -> string {     return __paint_rgb(str, .[style], fg_rgb, bg_rgb); } paint_rgb :: (     str: string,     style: []Text_Style = .[],     fg_rgb: Term_Rgb,     bg_rgb: Term_Rgb ) -> string {     return __paint_rgb(str, style, fg_rgb, bg_rgb); } paint_raw :: (codes: string, str: string) -> string {     return sprint(#insert TERM_ESCAPE_CODE, codes, str); } Term_Rgb :: struct {     r, g, b: u8 = 255, 255, 255; } Text_Style :: enum #specified {     NONE :: -1;     RESET               :: 0;     BOLD                :: 1;     FAINT               :: 2;   // not widely supported     ITALIC              :: 3;   // not widely supported     UNDERLINE           :: 4;     SLOW_BLINK          :: 5;   // less than 150 bpm     RAPID_BLINK         :: 6;   // not widely supported     SWAP_FG_BG          :: 7;     CONCEAL             :: 8;   // not widely supported     CROSSED_OUT         :: 9;   // not widely supported     PRIMARY_FONT        :: 10;     // omitting alternate fonts (11-19) since they aren't really supported anymore     FRAKTUR             :: 20;  // not widely supported     BOLD_OFF_OR_DOUBLE_UNDERLINE    :: 21;  // not widely supported     NORMAL_COLOR_OR_INTENSITY       :: 22;     ITALIC_OFF_FRAKTUR_OFF          :: 23;     UNDERLINE_OFF       :: 24;     BLINK_OFF           :: 25;     // Code 26 does nothing (https://vt100.net/docs/vt510-rm/SGR.html)     INVERSE_OFF         :: 27;     CONCEAL_OFF         :: 28;     CROSSED_OUT_OFF     :: 29;     FRAMED              :: 51;     ENCIRCLED           :: 52;     OVERLINED           :: 53;     ENCIRCLED_OFF_FRAMED_OFF :: 54;     OVERLINED_OFF       :: 55;     // 60 - 65  Ideograms hardly ever supported     // 90 - 107 Bright fg and bg color is non standard } Color_Foreground :: enum #specified {     NONE :: -1;     BLACK    :: 30;     RED      :: 31;     GREEN    :: 32;     YELLOW   :: 33;     BLUE     :: 34;     MAGENTA  :: 35;     CYAN     :: 36;     WHITE    :: 37;     EXTEND   :: 38;  // 5;<Color_Table>  OR  2;<r>;<g>;<b>     DEFAULT  :: 39; } Color_Background :: enum #specified {     NONE :: -1;     BLACK    :: 40;     RED      :: 41;     GREEN    :: 42;     YELLOW   :: 43;     BLUE     :: 44;     MAGENTA  :: 45;     CYAN     :: 46;     WHITE    :: 47;     EXTEND   :: 48;  // 5;<Color_Table>  OR  2;<r>;<g>;<b>     DEFAULT  :: 49; } Color_Table :: enum #specified {     NONE :: -1;     // Standard     ST_BLACK   :: 0;     ST_RED     :: 1;     ST_GREEN   :: 2;     ST_YELLOW  :: 3;     ST_BLUE    :: 4;     ST_PURPLE  :: 5;     ST_TEAL    :: 6;     ST_GRAY    :: 7;     // High Intensity     HI_GRAY    :: 8;     HI_RED     :: 9;     HI_GREEN   :: 10;     HI_YELLOW  :: 11;     HI_BLUE    :: 12;     HI_PURPLE  :: 13;     HI_TEAL    :: 14;     HI_WHITE   :: 15;     // Selected Subset     BLACK :: 16;     WHITE :: 231;     GRAY_DARK   :: 234;     GRAY_MID    :: 243;     GRAY_LIGHT  :: 250;     BLUE_DARK   :: 17;     BLUE_MID    :: 21;     BLUE_LIGHT  :: 45;     GREEN_DARK  :: 22;     GREEN_MID   :: 34;     GREEN_LIGHT :: 46;     RED_DARK    :: 52;     RED_MID     :: 124;     RED_LIGHT   :: 196;     ROSE_DARK   :: 163;     ROSE_MID    :: 201;     ROSE_LIGHT  :: 213;     MINT_DARK   :: 35;     MINT_MID    :: 78;     MINT_LIGHT  :: 84;     VIOLET_DARK  :: 53;     VIOLET_MID   :: 93;     VIOLET_LIGHT :: 141;     ORANGE_DARK  :: 166;     ORANGE_MID   :: 202;     ORANGE_LIGHT :: 214;     YELLOW_DARK  :: 220;     YELLOW_MID   :: 226;     YELLOW_LIGHT :: 228; } // leaving those constants public, maybe someone wants to use them TERM_ESCAPE_CODE :: #code "\u001b[%m%\u001b[0m"; FOREGROUND_COLOR_FROM_EXT_TABLE :: "38;5;"; BACKGROUND_COLOR_FROM_EXT_TABLE :: "48;5;"; #scope_file; // ---------------------------------------- // Internal Paint API // ------------------ __paint__build_style_str :: (style: []Text_Style) -> string {     buf_style: [..]string;     buf_style.allocator = temp;     for style  array_add(*buf_style, tprint("%", cast(int)it));     s_style := join(.. buf_style, ";",, temp);     return trim_right(s_style, ";",, temp); } __paint__to_term_code_args :: () -> string #expand {     s := join(.. `buf, ";",, temp);     s = trim_right(s, ";",, temp);     return paint_raw(s, `str); } __paint__buffer_add_term_codes :: (color_type: string, term_color_code: int) #expand {     `buf[`count] = tprint("%0%", color_type, term_color_code); `count += 1; } __paint__buffer_add_style :: () #expand {     if `style.count > 0 {         s_style := __paint__build_style_str(`style);         `buf[`count] = s_style;         `count += 1;     } } __paint :: (     str: string,     style: []Text_Style,     fg: int,     bg: int,     fg_code: string,     bg_code: string ) -> string {     buf: [3]string;     count: int;     __paint__buffer_add_style();     if fg != -1 { __paint__buffer_add_term_codes(fg_code, fg); }     if bg != -1 { __paint__buffer_add_term_codes(bg_code, bg); }     if count == 0 {         return paint_raw("", str);     }     return __paint__to_term_code_args(); } __paint :: (     str: string,     style: []Text_Style,     fg: $A,     bg: $B,     fg_code: string,     bg_code: string ) -> string {     buf: [3]string;     count: int;     __paint__buffer_add_style();     if fg != .NONE { __paint__buffer_add_term_codes(fg_code, cast(int)fg); }     if bg != .NONE { __paint__buffer_add_term_codes(bg_code, cast(int)bg); }     if count == 0 {         return paint_raw("", str);     }     return __paint__to_term_code_args(); } __paint_rgb :: (     str: string,     style: []Text_Style,     fg_rgb: Term_Rgb,     bg_rgb: Term_Rgb ) -> string {     buf: [3]string;     if style.count > 0 {         buf[0] = __paint__build_style_str(style);     }     buf[1] = tprint("38;2;%;%;%", fg_rgb.r, fg_rgb.g, fg_rgb.b);     buf[2] = tprint("48;2;%;%;%", bg_rgb.r, bg_rgb.g, bg_rgb.b);     s := join(.. buf, ";",, temp);     s = trim_right(s, ";",, temp);     return paint_raw(s, str); } diff --git a/termcolors/module.jai b/termcolors/module.jai new file mode 100644 index 0000000..dce250c --- /dev/null +++ b/termcolors/module.jai @@ -0,0 +1,5 @@ /*     License and docs are in `lib.jai` */ #load "lib.jai"; diff --git a/test/test.jai b/test/test.jai new file mode 100644 index 0000000..10f36af --- /dev/null +++ b/test/test.jai @@ -0,0 +1,65 @@ #import "Basic"()(     MEMORY_DEBUGGER = MEMORY_DEBUGGER ); #import "termcolors"; MEMORY_DEBUGGER :: true; main :: () {     #if MEMORY_DEBUGGER defer report_memory_leaks();     context.allocator = temp;     log("--- paint() -----------------------------");     log(paint("1 Plain"));     print("\n");     log(paint("2 Bold, fg Black", .BOLD, .BLACK));     print("\n");     log(paint("3 Bold, fg Black, bg White", .BOLD, .BLACK, .WHITE));     print("\n");     log(paint("4 Bold, Underline, Italic, fg Black, bg White", .[.BOLD, .UNDERLINE, .ITALIC], .BLACK, .WHITE));     print("\n\n");     log("--- paint_ex() -----------------------------");     log(paint_ex("5 Underline, fg GreenDark, bg OrangeLight", .UNDERLINE, .GREEN_DARK, .ORANGE_LIGHT));     print("\n");     log(paint_ex("6 Underline, fg GreenDark", .UNDERLINE, .GREEN_DARK));     print("\n");     log(paint_ex("7 Bold, Underline, Italic, fg GreenDark, bg OrangeLight", .[.BOLD, .UNDERLINE, .ITALIC], .GREEN_DARK, .ORANGE_LIGHT));     print("\n");     log(paint_ex("8 Underline, fg 84, bg 124", .UNDERLINE, 84, 124));     print("\n");     log(paint_ex("9 Bold, Underline, Italic, 84, 124", .[.BOLD, .UNDERLINE, .ITALIC], 84, 124));     print("\n\n");     log("--- paint_ex_custom() -----------------------------");     My_Colors :: enum #specified {         NONE :: -1;         COL1 :: 95;         COL2 :: 201;     }     log(paint_ex_custom("10 Underline, fg Custom, bg Custom", .UNDERLINE, My_Colors.COL1, My_Colors.COL2));     print("\n\n");     log("--- paint_rgb() -----------------------------");     log(paint_rgb("11 Underline, fg rgb(255,0,0), bg rgb(0,0,255)", .UNDERLINE, .{ 255, 0, 0 }, .{ 0, 0, 255 }));     print("\n");     log(paint_rgb("12 Bold, Underline, Italic, fg rgb(255,0,0), bg rgb(0,0,255)", .[.BOLD, .UNDERLINE, .ITALIC], .{ 255, 0, 0 }, .{ 0, 0, 255 }));     print("\n\n");     log("--- paint_raw_temp() -----------------------------");     log(paint_raw("1;3;32;45", "13 tbd tbd tbd"));     print("\n\n"); }