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