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