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 }