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