1 /// Formatting and util to work with BASH
2 module sily.bashfmt;
3 
4 import std.conv : to;
5 import std.stdio : write, writef;
6 
7 static this() {
8     version(windows) {
9         import core.stdc.stdlib: exit;
10         exit(2);
11     }
12 }
13 
14 // LINK: https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
15 
16 /// Short alias to formatting enum
17 alias FG = Foreground;
18 /// Ditto
19 alias BG = Background;
20 /// Ditto
21 alias FM = Formatting;
22 /// Ditto
23 alias FR = FormattingReset;
24 
25 /// Contains escape sequences for foreground colors
26 enum Foreground : string {
27     reset = "\033[39m",
28     black = "\033[30m",
29     red = "\033[31m",
30     green = "\033[32m",
31     yellow = "\033[33m",
32     blue = "\033[34m",
33     magenta = "\033[35m",
34     cyan = "\033[36m",
35     ltgray = "\033[37m",
36     dkgray = "\033[90m",
37     ltred = "\033[91m",
38     ltgreen = "\033[92m",
39     ltyellow = "\033[93m",
40     ltblue = "\033[94m",
41     ltmagenta = "\033[95m",
42     ltcyan = "\033[96m",
43     white = "\033[97m",
44 }
45 
46 /// Contains escape sequences for background colors
47 enum Background : string {
48     reset = "\033[49m",
49     black = "\033[40m",
50     red = "\033[41m",
51     green = "\033[42m",
52     yellow = "\033[43m",
53     blue = "\033[44m",
54     magenta = "\033[45m",
55     cyan = "\033[46m",
56     ltgray = "\033[47m",
57     dkgray = "\033[100m",
58     ltred = "\033[101m",
59     ltgreen = "\033[102m",
60     ltyellow = "\033[103m",
61     ltblue = "\033[104m",
62     ltmagenta = "\033[105m",
63     ltcyan = "\033[106m",
64     white = "\033[107m"
65 }
66 
67 /// Contains escape sequences for string formatting (bold, italics)
68 enum Formatting : string {
69     bold = "\033[1m",
70     dim = "\033[2m",
71     italics = "\033[3m",
72     uline = "\033[4m",
73     blink = "\033[5m",
74     inverse = "\033[7m",
75     hidden = "\033[8m",
76     striked = "\033[9m",
77     dline = "\033[21m",
78     cline = "\033[4:3m"
79 }
80 
81 /// Contains escape sequences to reset string formatting
82 enum FormattingReset : string {
83     reset = "\033[0m",
84     fullreset = "\033[m",
85 
86     bold = "\033[21m",
87     dim = "\033[22m",
88     italics = "\033[22m",
89     uline = "\033[24m",
90     blink = "\033[25m",
91     inverse = "\033[27m",
92     hidden = "\033[28m",
93     striked = "\033[29m",
94     dline = "\033[24m",
95     cline = "\033[4:0m"
96 }
97 
98 /* --------------------------------- OUTPUT --------------------------------- */
99 
100 /** 
101 Casts args to string and writes to stdout
102 Intended to be used to print formatting
103 ---
104 fwrite("White text", FG.red, "Red text", FG.reset, BG.red, "Red background", FR.fullreset);
105 ---
106 Params:
107   args = Text or one of formatting strings
108 */
109 void fwrite(A...)(A args) {
110     foreach (arg; args) {
111         write(cast(string) arg);
112     }
113 }
114 
115 /** 
116 Casts args to string and writes to stdout with `\n` at the end
117 Intended to be used to print formatting
118 ---
119 fwriteln("White text", FG.red, "Red text", FG.reset, BG.red, "Red background", FR.fullreset);
120 ---
121 Params:
122   args = Text or one of formatting strings
123 */
124 void fwriteln(A...)(A args) {
125     foreach (arg; args) {
126         write(cast(string) arg);
127     }
128     write("\n");
129 }
130 
131 /* ------------------------------- LINE ERASE ------------------------------- */
132 
133 /** 
134 Erases `num` lines in terminal starting with current.
135 Params:
136   num = Number of lines to erase
137 */
138 void eraseLines(int num) {
139     if (num < 1) return;
140     eraseCurrentLine();
141     --num;
142 
143     while (num) {
144         cursorMoveUpScroll();
145         eraseCurrentLine();
146         --num;
147     }
148 }
149 
150 /// Fully erases current line 
151 void eraseCurrentLine() {
152     write("\033[2K");
153 }
154 
155 /// Erases text from start of current line to cursor 
156 void eraseLineLeft() {
157     write("\033[1K");
158 }
159 
160 /// Erases text from cursor to end of current line
161 void eraseLineRight() {
162     write("\033[K");
163 }
164 
165 /* --------------------------------- CURSOR --------------------------------- */
166 
167 import sily.terminal: terminalModeSetRaw, terminalModeReset, getch;
168 import sily.vector: uvec2;
169 
170 /// Returns cursor position
171 uvec2 cursorGetPosition() {
172     uvec2 v;
173     char[] buf = new char[](30);
174     int i, pow;
175     char ch;
176 
177     terminalModeSetRaw();
178     writef("\033[6n");
179 
180     for (i = 0, ch = 0; ch != 'R'; i++) {
181         int r = getch(); ch = cast(char) r;
182         // in case of getting stuck
183         if (r == 17) {terminalModeReset(); return v;}
184         if (!r) {
185             // error("Error reading response"); moveCursorTo(0);
186             terminalModeReset(); return v;
187         }
188         buf[i] = ch;
189         // if (i != 0) { 
190         //     import std.format: format;
191         //     trace("buf[%d]: %c %d".format(i, ch, ch)); moveCursorTo(0);
192         // }
193     }
194     if (i < 2) {
195         terminalModeReset();
196         // error("Incorrect response size"); moveCursorTo(0);
197         return v;
198     }
199 
200     for (i -= 2, pow = 1; buf[i] != ';'; --i, pow *= 10) {
201         v.x = v.x + (buf[i] - '0') * pow;
202     }
203     for (--i, pow = 1; buf[i] != '['; --i, pow *= 10) {
204         v.y = v.y + (buf[i] - '0') * pow;
205     }
206 
207     terminalModeReset();
208     return v;
209 }
210 
211 /** 
212 Moves cursor in terminal to `{x, y}` or to `x`. **COORDINATES START FROM 1**
213 Params:
214   x = Column to move to
215   y = Row to move to
216 */
217 void cursorMoveTo(int x, int y) {
218     writef("\033[%d;%df", y, x);
219 }
220 /// Ditto
221 void cursorMoveTo(uvec2 pos) { 
222     writef("\033[%d;%df", pos.y, pos.x);
223 }
224 /// Ditto
225 void cursorMoveTo(int x) {
226     writef("\033[%dG", x);
227 }
228 
229 /// Moves cursor in terminal to `{1, 1}`
230 void cursorMoveHome() {
231     writef("\033[H");
232 }
233 
234 /** 
235 Moves cursor in terminal up by `lineAmount`
236 Params: 
237   lineAmount = int
238 */
239 void cursorMoveUp(int lineAmount = 1) {
240     writef("\033[%dA", lineAmount);
241 }
242 
243 /// Moves cursor in terminal up by 1 and scrolls if needed
244 void cursorMoveUpScroll() {
245     writef("\033M");
246 }
247 
248 /** 
249 Moves cursor in terminal up by`lineAmount` and sets cursor X to 1
250 Params: 
251   lineAmount = int
252 */
253 void cursorMoveUpStart(int lineAmount = 1) {
254     writef("\033[%dF", lineAmount);
255 }
256 
257 /** 
258 Moves cursor in terminal down by `lineAmount`
259 Params: 
260   lineAmount = int
261  */
262 void cursorMoveDown(int lineAmount = 1) {
263     writef("\033[%dB", lineAmount);
264 }
265 
266 /** 
267 Moves cursor in terminal down by`lineAmount` and sets cursor X to 1
268 Params: 
269   lineAmount = int
270 */
271 void cursorMoveDownStart(int lineAmount = 1) {
272     writef("\033[%dE", lineAmount);
273 }
274 
275 /** 
276 Moves cursor in terminal right by `columnAmount`
277 Params: 
278   columnAmount = int
279 */
280 void cursorMoveRight(int columnAmount = 1) {
281     writef("\033[%dC", columnAmount);
282 }
283 
284 /** 
285 Moves cursor in terminal left by `columnAmount`
286 Params: 
287   columnAmount = int
288 */
289 void cursorMoveLeft(int columnAmount = 1) {
290     writef("\033[%dD", columnAmount);
291 }
292 
293 /// Saves/Restores cursor position to be restored later (DEC)
294 void cursorSavePosition() {
295     write("\0337");
296 }
297 
298 /// Ditto
299 void cursorRestorePosition() {
300     write("\0338");
301 }
302 
303 /// Saves/Restores cursor position to be restored later (SCO). **PREFER DEC (`saveCursorPosition`) VERSION INSTEAD.**
304 void cursorSavePositionSCO() {
305     write("\033[s");
306 }
307 
308 /// Ditto
309 void cursorRestorePositionSCO() {
310     write("\033[u");
311 }
312 
313 /// Hides cursor. Does not reset position
314 void cursorHide() {
315     write("\033[?25l");
316 }
317 
318 /// Shows cursor. Does not reset position
319 void cursorShow() {
320     write("\033[?25h");
321 }
322 
323 /* --------------------------------- SCREEN --------------------------------- */
324 
325 /// Clears terminal screen and resets cursor position
326 void screenClear() {
327     write("\033[2J");
328     cursorMoveHome();
329 }
330 
331 /// Clears terminal screen
332 void screenClearOnly() {
333     write("\033[2J");
334 }
335 
336 /// Enabled/Disables Alt Buffer. **PREFER `screenEnableAltBuffer` OR `screenDisableAltBuffer` INSTEAD.**
337 void screenSave() {
338     write("\033[?47h");
339 }
340 /// Ditto
341 void screenRestore() {
342     write("\033[?47l");
343 }
344 
345 /// Enabled/Disables alternative screen buffer. 
346 void screenEnableAltBuffer() {
347     write("\033[?1049h");
348 }
349 /// Ditto
350 void screenDisableAltBuffer() {
351     write("\033[?1049l");
352 }
353 
354 /// Hard resets terminal. Not recommended to use
355 void screenHardReset() {
356     write("\033c");
357 }
358 
359 /// Sets terminal title that's going to last until program termination
360 void setTitle(string title) {
361     write("\033]0;" ~ title ~ "\007");
362 }
363 
364 /// Rings audio bell
365 void bell() {
366     write("\a");
367 }
368 
369 /** 
370 Intended to be used in SIGINT callback
371 Resets all formatting and shows cursor
372 */
373 void cleanTerminalState() nothrow @nogc @system {
374     import core.stdc.stdio: printf;
375     printf("\033[?1049l\033[?25h\033[m");
376 }