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 }