Skip to content

Commit 81f150c

Browse files
committed
Control color output with C2_COLORS environment variable
* use style names instead of hard-coded color names * customize color output via environment variable `C2_COLORS`: eg: `C2_COLORS=none`, `C2_COLORS="blue:bright-blue"`, `C2_COLORS="error:bright-blue"`, `C2_COLORS="error:#d0d0d0"` * use `console` to output error messages in `source_mgr` and `c2recipe_parser`. * use cache to avoid multiple calls to `unix.isatty()` and `stdlib.getenv()` * share global colors with plugins * simplify error formating in **source_mgr.c2** * add c2cat color customisation
1 parent 67f32b6 commit 81f150c

File tree

14 files changed

+398
-73
lines changed

14 files changed

+398
-73
lines changed

ast/utils.c2

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ public type Globals struct @(opaque) {
120120
#if AstStatistics
121121
Stats stats;
122122
#endif
123+
Color[15] colors;
123124
}
124125

125126
// The only globals for AST are here, since they must be set explicitly for plugins!!
@@ -129,7 +130,24 @@ Globals* globals;
129130
public fn Globals* getGlobals() { return globals; }
130131

131132
// only used by plugins
132-
public fn void setGlobals(Globals* g) @(unused) { globals = g; }
133+
public fn void setGlobals(Globals* g) @(unused) {
134+
globals = g;
135+
col_Normal = g.colors[0];
136+
col_Error = g.colors[1];
137+
col_Warning = g.colors[2];
138+
col_Keyword = g.colors[3];
139+
col_Identifier = g.colors[4];
140+
col_Literal = g.colors[5];
141+
col_Comment = g.colors[6];
142+
col_Stmt = g.colors[7];
143+
col_Decl = g.colors[8];
144+
col_Expr = g.colors[9];
145+
col_Attr = g.colors[10];
146+
col_Template = g.colors[11];
147+
col_Type = g.colors[12];
148+
col_Value = g.colors[13];
149+
col_Calc = g.colors[14];
150+
}
133151

134152
// wordsize in bytes, must NOT be called from Plugin!
135153
public fn void initialize(Context* c, string_pool.Pool* astPool, u32 wordsize, bool use_color) {
@@ -176,22 +194,43 @@ public fn void initialize(Context* c, string_pool.Pool* astPool, u32 wordsize, b
176194
globals.builtinType_baseTypes[ISize] = Int64;
177195
globals.builtinType_baseTypes[USize] = UInt64;
178196
}
197+
198+
globals.colors[0] = color.getConfigColor("normal", color.Normal);
199+
globals.colors[1] = color.getConfigColor("error", color.Red);
200+
globals.colors[2] = color.getConfigColor("warning", color.Yellow);
201+
globals.colors[3] = color.getConfigColor("keyword", color.Green);
202+
globals.colors[4] = color.getConfigColor("identifier", color.Cyan);
203+
globals.colors[5] = color.getConfigColor("literal", color.Cyan);
204+
globals.colors[6] = color.getConfigColor("comment", color.Cyan);
205+
globals.colors[7] = color.getConfigColor("ast.stmt", color.Bmagenta);
206+
globals.colors[8] = color.getConfigColor("ast.decl", color.Bgreen);
207+
globals.colors[9] = color.getConfigColor("ast.expr", color.Bmagenta);
208+
globals.colors[10] = color.getConfigColor("ast.attr", color.Blue);
209+
globals.colors[11] = color.getConfigColor("ast.template", color.Green);
210+
globals.colors[12] = color.getConfigColor("ast.type", color.Green);
211+
globals.colors[13] = color.getConfigColor("ast.value", color.Bcyan);
212+
globals.colors[14] = color.getConfigColor("ast.calc", color.Yellow); // all calculated values
213+
214+
setGlobals(globals);
179215
}
180216

181217
public fn void deinit(bool print_stats) {
218+
Globals* g = globals;
219+
if (!g) return;
182220
#if AstStatistics
183-
if (print_stats) globals.stats.dump();
221+
if (print_stats) g.stats.dump();
184222
#endif
185-
globals.names_pool = nil;
186-
globals.ast_count = 0;
187-
globals.ast_capacity = 0;
188-
stdlib.free(globals.ast_list);
189-
globals.ast_list = nil;
190-
globals.pointers.clear();
191-
globals.string_types.clear();
192-
globals.dump_buf.free();
193-
stdlib.free(globals);
223+
g.names_pool = nil;
224+
g.ast_count = 0;
225+
g.ast_capacity = 0;
226+
stdlib.free(g.ast_list);
227+
g.ast_list = nil;
228+
g.pointers.clear();
229+
g.string_types.clear();
230+
g.dump_buf.free();
231+
stdlib.free(g);
194232
globals = nil;
233+
color.freeConfigColors();
195234
}
196235

197236
public fn u32 getWordSize() {
@@ -319,9 +358,14 @@ Color col_Template = Color.Green;
319358
//Color col_Cast = Color.Red;
320359
Color col_Type = Color.Green;
321360
Color col_Value = Color.Bcyan;
322-
Color col_Error = Color.Red;
361+
public Color col_Error = Color.Red;
323362
Color col_Calc = Color.Yellow; // all calculated value
324-
Color col_Normal = Color.Normal;
363+
public Color col_Normal = Color.Normal;
364+
public Color col_Warning = color.Yellow;
365+
public Color col_Keyword = color.Green;
366+
public Color col_Identifier = color.Cyan;
367+
public Color col_Literal = color.Cyan;
368+
public Color col_Comment = color.Cyan;
325369

326370
public type AttrHandlerFn fn bool (void* arg, Decl* d, const Attr* a);
327371

ast_utils/color.c2

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
*/
1515

1616
module color;
17-
import unistd;
17+
18+
import stdlib local;
19+
import string local;
20+
import unistd local;
1821

1922
public type Color enum u8 {
2023
None,
@@ -35,6 +38,9 @@ public type Color enum u8 {
3538
Bcyan,
3639
White,
3740
Normal,
41+
42+
CustomStart,
43+
CustomEnd = Color.CustomStart + 15,
3844
}
3945

4046
public const Color Black = Black;
@@ -55,7 +61,7 @@ public const Color Bcyan = Bcyan;
5561
public const Color White = White;
5662
public const Color Normal = Normal;
5763

58-
const char*[elemsof(Color)] defaultColors = {
64+
const char*[] standardColors = {
5965
[Color.None] = "",
6066
[Color.Black] = "\033[0;30m",
6167
[Color.Red] = "\033[0;31m",
@@ -76,8 +82,27 @@ const char*[elemsof(Color)] defaultColors = {
7682
[Color.Normal] = "\033[0m",
7783
}
7884

85+
i8 use_color = -1;
86+
const char* c2_colors;
87+
char[elemsof(Color)][32] defaultColors;
88+
7989
public fn bool useColor() {
80-
return unistd.isatty(1);
90+
if (use_color < 0) {
91+
use_color = isatty(1) != 0;
92+
if (use_color) {
93+
c2_colors = getenv("C2_COLORS");
94+
if (c2_colors && !strcmp(c2_colors, "none")) {
95+
use_color = 0;
96+
c2_colors = nil;
97+
} else {
98+
for (u32 i = 0; i < elemsof(standardColors); i++) {
99+
strcpy(defaultColors[i], standardColors[i]);
100+
}
101+
if (c2_colors) customizeStandardColors(c2_colors);
102+
}
103+
}
104+
}
105+
return use_color;
81106
}
82107

83108
public fn const char* Color.str(Color col) {

ast_utils/color_config.c2

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/* Copyright 2022-2025 Bas van den Berg, Charlie Gordon
2+
*
3+
* Licensed under the Apache License, Version 2.0 (the "License");
4+
* you may not use this file except in compliance with the License.
5+
* You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
module color;
17+
18+
import ctype local;
19+
import stdio local;
20+
import stdlib local;
21+
import string local;
22+
23+
const char*[] standardColorNames = {
24+
[Color.Black] = "black",
25+
[Color.Red] = "red",
26+
[Color.Green] = "green",
27+
[Color.Yellow] = "yellow",
28+
[Color.Blue] = "blue",
29+
[Color.Magenta] = "magenta",
30+
[Color.Cyan] = "cyan",
31+
[Color.Grey] = "grey",
32+
[Color.Darkgrey] = "darkgrey",
33+
[Color.Bred] = "bred",
34+
[Color.Bgreen] = "bgreen",
35+
[Color.Byellow] = "byellow",
36+
[Color.Bblue] = "bblue",
37+
[Color.Bmagenta] = "bmagenta",
38+
[Color.Bcyan] = "bcyan",
39+
[Color.White] = "white",
40+
[Color.Normal] = "normal",
41+
}
42+
43+
fn Color findStandardColor(const char* name, bool use_custom = false) {
44+
if (!name) return Color.None;
45+
for (u32 i = 0; i < elemsof(standardColorNames); i++) {
46+
if (!strcmp(name, standardColorNames[i]))
47+
return (Color)i;
48+
}
49+
if (use_custom && !strncmp(name, "custom", 6)) {
50+
i32 n = atoi(name + 6);
51+
if (n > 0 && n <= Color.CustomEnd - Color.CustomStart + 1)
52+
return (Color)(Color.CustomStart + n - 1);
53+
}
54+
return Color.None;
55+
}
56+
57+
fn bool setPaletteColor(Color col, const char *val) {
58+
u32 pal;
59+
if (sscanf(val, "%*1[pP]%u", &pal) == 1) {
60+
snprintf(defaultColors[col], elemsof(defaultColors[col]), "\033[38;5;%dm", pal);
61+
return true;
62+
}
63+
u32 r, g, b;
64+
if (sscanf(val, "#%2x%2x%2x", &r, &g, &b) == 3
65+
|| sscanf(val, "rgb(%u,%u,%u)", &r, &g, &b) == 3) {
66+
snprintf(defaultColors[col], elemsof(defaultColors[col]), "\033[38;2;%d;%d;%dm", r, g, b);
67+
return true;
68+
}
69+
return false;
70+
}
71+
72+
fn bool setColorString(Color col, const char *val) {
73+
if (!val || *val == '\0') {
74+
*defaultColors[col] = '\0';
75+
return true;
76+
}
77+
if (!strcmp(val, "default")) {
78+
*defaultColors[col] = '\0';
79+
if (col < elemsof(standardColors) && standardColors[col])
80+
strcpy(defaultColors[col], standardColors[col]);
81+
return true;
82+
}
83+
if (Color col1 = findStandardColor(val)) {
84+
strcpy(defaultColors[col], standardColors[col1]);
85+
return true;
86+
}
87+
return setPaletteColor(col, val);
88+
}
89+
90+
// Normalize a color name into array dest.
91+
// s and dest can point to the same array
92+
fn char* normalizeColor(char *dest, u32 size, const char* s) {
93+
if (!size) return nil;
94+
u32 len = size - 1;
95+
u32 i = 0;
96+
if (s) {
97+
for (const char* p = s; *p && i < len; p++) {
98+
char c = (char)tolower(*p);
99+
if (c != '-' && c != '_') {
100+
if (i < len) dest[i++] = c;
101+
}
102+
}
103+
}
104+
dest[i] = '\0';
105+
if (*dest == 'b' && !strncmp(dest + 1, "right", 5)) {
106+
memmove(dest + 1, dest + 6, i + 1 - 6);
107+
}
108+
return dest;
109+
}
110+
111+
fn void customizeStandardColors(const char *p) {
112+
if (!p) return;
113+
114+
char[16] name;
115+
char[16] val;
116+
while (getConfigEntry(name, elemsof(name), val, elemsof(val), &p)) {
117+
// check for standard color customisation
118+
if (Color col = findStandardColor(normalizeColor(name, elemsof(name), name), true)) {
119+
setColorString(col, normalizeColor(val, elemsof(val), val));
120+
}
121+
}
122+
}
123+
124+
fn bool getConfigEntry(char* buf1, u32 size1, char* buf2, u32 size2, const char** pp) {
125+
const char *p = *pp;
126+
for (;;) {
127+
while (isspace(*p))
128+
p++;
129+
if (*p == '\0' || *p == '[')
130+
return false;
131+
if (*p != '#')
132+
break;
133+
while (*p && *p++ != '\n')
134+
continue;
135+
}
136+
u32 i = 0;
137+
u32 j = 0;
138+
while (*p && *p != '=' && *p != ':' && *p != ';' && !isspace(*p)) {
139+
char c = *p++;
140+
if (i + 1 < size1)
141+
buf1[i++] = c;
142+
if (j + 1 < size2)
143+
buf2[j++] = c;
144+
}
145+
if (size1) buf1[i] = '\0';
146+
if (size2) buf2[j] = '\0';
147+
while (*p == ' ') p++;
148+
if (*p == '=' || *p == ':') {
149+
p++;
150+
while (*p == ' ') p++;
151+
j = 0;
152+
while (*p && *p != ';' && *p != '\n') {
153+
char c = *p++;
154+
if (j + 1 < size2) buf2[j++] = c;
155+
}
156+
if (size2) buf2[j] = '\0';
157+
}
158+
while (*p) {
159+
char c = *p++;
160+
if (c == '\n' || c == ';')
161+
break;
162+
}
163+
*pp = p;
164+
return true;
165+
}
166+
167+
fn Color convertColor(const char *val, Color def) {
168+
if (!val || *val == '\0')
169+
return None;
170+
171+
if (Color col = findStandardColor(val))
172+
return col;
173+
174+
//if (!strcmp(val, "default"))
175+
// return def;
176+
177+
for (u32 i = Color.CustomStart; i <= Color.CustomEnd; i++) {
178+
if (!defaultColors[i]) {
179+
Color col = (Color)i;
180+
if (setPaletteColor(col, val)) return col;
181+
// TODO: complain about unknown color
182+
return def;
183+
}
184+
}
185+
// TODO: complain about out of custom colors
186+
return def;
187+
}
188+
189+
public fn Color getConfigColor(const char* cat, Color def) {
190+
if (!use_color)
191+
return None;
192+
if (c2_colors) {
193+
const char *p = c2_colors;
194+
char[16] cat1;
195+
char[16] style;
196+
char[16] val;
197+
if (!strcmp(p, "none"))
198+
return None;
199+
normalizeColor(cat1, elemsof(cat1), cat);
200+
while (getConfigEntry(style, elemsof(style), val, elemsof(val), &p)) {
201+
if (!strcmp(normalizeColor(style, elemsof(style), style), cat1))
202+
return convertColor(normalizeColor(val, elemsof(val), val), def);
203+
}
204+
}
205+
return def;
206+
}
207+
208+
public fn void freeConfigColors() {
209+
for (u32 i = Color.CustomStart; i <= Color.CustomEnd; i++) {
210+
*defaultColors[i] = '\0';
211+
}
212+
}

0 commit comments

Comments
 (0)