1 module dstatus.status; 2 3 import std.conv : text, to; 4 import std.format : format; 5 import std.range : repeat, take; 6 import std.stdio : File, stdout, stderr; 7 import std.string : leftJustify, rightJustify; 8 9 import dstatus.terminal; 10 11 class Status { 12 private { 13 size_t _writeLength = 0; 14 size_t _prevReportLength = 0; 15 TerminalPosition _originalPosition; 16 TerminalPosition _currentPosition; 17 } 18 19 File output; 20 21 this() { 22 output = stderr; 23 anchor(); 24 } 25 26 private TerminalPosition getPosition() { 27 return new TerminalPosition(output); 28 } 29 30 private void _write(string txt) { 31 /* Reset the cursor to the current reporting position */ 32 _currentPosition.restore(); 33 34 // Write the new text 35 output.write(txt); 36 37 /* If text is shorter than the previous length, 38 overwrite the remainder with spaces. */ 39 if (txt.length < _prevReportLength) { 40 auto endPosition = getPosition(); 41 scope(exit) endPosition.restore(); 42 43 auto remainderLength = _prevReportLength - txt.length; 44 output.write(' '.repeat(remainderLength)); 45 } 46 } 47 48 /* Anchor to the current cursor position. */ 49 final void anchor() { 50 _currentPosition = _originalPosition = getPosition(); 51 _writeLength = 0; 52 _prevReportLength = 0; 53 } 54 55 final void clear() { 56 /* Reset the cursor to its original starting position. */ 57 _originalPosition.restore(); 58 59 /* Clear all text written by us */ 60 auto clearLength = _writeLength + _prevReportLength; 61 output.write(' '.repeat(clearLength)); 62 63 /* Reset the cursor to its original starting position. */ 64 _originalPosition.restore(); 65 66 _writeLength = 0; 67 _prevReportLength = 0; 68 } 69 70 final void write(T...)(T args) { 71 auto txt = text(args); 72 73 _write(txt); 74 _currentPosition = getPosition(); 75 76 _writeLength += txt.length; 77 _prevReportLength = 0; 78 } 79 80 final void report(T...)(T args) { 81 auto txt = text(args); 82 83 _write(txt); 84 85 // Store the length of the new text 86 _prevReportLength = txt.length; 87 } 88 } 89 90 auto status() { 91 return new Status(); 92 } 93 94 95 @safe: 96 97 pure string makeFixedWidth(string truncatedSuffix = "...", alias justify = leftJustify, T...)(in size_t width, in T args) { 98 auto str = text(args); 99 100 if (str.length > width) { 101 return "%s%s".format(str.take(width - truncatedSuffix.length), truncatedSuffix); 102 } 103 104 return justify(str, width); 105 } 106 107 unittest { 108 enum testString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 109 110 assert(makeFixedWidth(testString.length, testString) == testString); 111 assert(makeFixedWidth(10, testString) == "ABCDEFG..."); 112 assert(makeFixedWidth(27, testString) == "ABCDEFGHIJKLMNOPQRSTUVWXYZ "); 113 assert(makeFixedWidth!("")(10, testString) == "ABCDEFGHIJ"); 114 }