1 module jcli.text.helptext; 2 3 import jcli.text, std; 4 5 struct HelpTextDescription 6 { 7 uint indent; 8 string text; 9 } 10 11 struct HelpText 12 { 13 enum ARG_NAME_PERCENT = 0.1; 14 enum ARG_GUTTER_PERCENT = 0.05; 15 enum ARG_DESC_PERCENT = 0.3; 16 17 private 18 { 19 TextBuffer _text; 20 uint _argNameWidth; 21 uint _argDescWidth; 22 uint _argGutterWidth; 23 uint _rowCursor; 24 string _cached; 25 } 26 27 static HelpText make(uint width) 28 { 29 HelpText t; 30 t._text = new TextBuffer(width, TextBuffer.AUTO_GROW); 31 t._argNameWidth = cast(uint)((cast(double)width) * ARG_NAME_PERCENT).round; 32 t._argDescWidth = cast(uint)((cast(double)width) * ARG_DESC_PERCENT).round; 33 t._argGutterWidth = cast(uint)((cast(double)width) * ARG_GUTTER_PERCENT).round; 34 return t; 35 } 36 37 void addLine(string text) 38 { 39 Vector _1; 40 this._text.setString( 41 Rect(0, this._rowCursor, this._text.width, this._rowCursor+1), 42 text, 43 _1, 44 ); 45 this._rowCursor++; 46 } 47 48 void addLineWithPrefix(string prefix, string text, AnsiStyleSet prefixStyle = AnsiStyleSet.init) 49 { 50 Vector lastChar; 51 this._text.setString( 52 Rect(0, this._rowCursor, prefix.length.to!int, this._rowCursor + 1), 53 prefix, 54 lastChar, 55 prefixStyle 56 ); 57 this._text.setString( 58 Rect(lastChar.x + 1, lastChar.y, this._text.width, this._rowCursor + 1), 59 text, 60 lastChar 61 ); 62 this._rowCursor = lastChar.y + 1; 63 } 64 65 void addHeaderWithText(string header, string text) 66 { 67 Vector lastChar; 68 this._text.setString( 69 Rect(0, this._rowCursor, this._text.width, this._rowCursor + 1), 70 header, 71 lastChar, 72 AnsiStyleSet.init.style(AnsiStyle.init.bold) 73 ); 74 this._rowCursor++; 75 this._text.setString( 76 Rect(4, this._rowCursor, this._text.width, this._text.height), 77 text, 78 lastChar 79 ); 80 this._rowCursor = lastChar.y + 2; 81 } 82 83 void addHeader(string header) 84 { 85 Vector _1; 86 this._text.setString( 87 Rect(0, this._rowCursor, this._text.width, this._rowCursor + 1), 88 header, 89 _1, 90 AnsiStyleSet.init.style(AnsiStyle.init.bold) 91 ); 92 this._rowCursor += 1; 93 } 94 95 void addArgument(string name, HelpTextDescription[] description) 96 { 97 Vector namePos; 98 this._text.setString( 99 Rect(4, this._rowCursor, this._argNameWidth, this._rowCursor + 1), 100 name, 101 namePos 102 ); 103 104 Vector descPos = Vector(0, this._rowCursor); 105 foreach(desc; description) 106 { 107 const indent = desc.indent * 4; 108 this._text.setString( 109 Rect(this._argNameWidth + this._argGutterWidth + indent, descPos.y, this._argNameWidth + this._argGutterWidth + this._argDescWidth, descPos.y + 1), 110 desc.text, 111 descPos 112 ); 113 } 114 this._rowCursor = max(namePos.y + 1, descPos.y + 1); 115 } 116 117 string finish() 118 { 119 if(this._cached) 120 return this._cached; 121 122 Appender!(char[]) output; 123 this._text.onRefresh = (row, cells) 124 { 125 AnsiStyleSet style; 126 foreach(cell; cells) 127 { 128 if(cell.style != style && g_jcliTextUseColour) 129 { 130 style = cell.style; 131 output.put(ANSI_COLOUR_RESET); 132 output.put(ANSI_CSI); 133 134 char[AnsiStyleSet.MAX_CHARS_NEEDED] chars; 135 output.put(style.toSequence(chars)); 136 output.put(ANSI_COLOUR_END); 137 } 138 output.put(cell.ch[0..cell.chLen]); 139 } 140 output.put(ANSI_COLOUR_RESET); 141 output.put('\n'); 142 }; 143 this._text.refresh(); 144 this._cached = output.data.assumeUnique; 145 return this._cached; 146 } 147 }