Skip to content

Commit f728c0a

Browse files
authored
new: added Help class that prints a help text (#11)
1 parent 8144096 commit f728c0a

File tree

8 files changed

+1170
-3
lines changed

8 files changed

+1170
-3
lines changed

.ci/macos-latest/resource-config.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"resources":{
3+
"includes":[{
4+
"pattern":"\\Qcom/sun/jna/darwin-x86-64/libjnidispatch.jnilib\\E"
5+
}]},
6+
"bundles":[]
7+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"resources":{
3+
"includes":[{
4+
"pattern":"\\Qcom/sun/jna/linux-x86-64/libjnidispatch.so\\E"
5+
}]},
6+
"bundles":[]
7+
}
+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"resources":{
3+
"includes":[{
4+
"pattern":"\\Qcom/sun/jna/win32-x86-64/jnidispatch.dll\\E"
5+
}]},
6+
"bundles":[]
7+
}

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ test-results-native/
1414
Thumbs.db
1515

1616
# GraalVM native build configuration files
17-
src/main/resources/META-INF/native-image
17+
/src/test/resources/META-INF/native-image/resource-config.json
18+

pom.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@
5050
<dependency>
5151
<groupId>io.github.sttk</groupId>
5252
<artifactId>linebreak</artifactId>
53-
<version>0.1.3</version>
53+
<version>0.1.4</version>
5454
<scope>compile</scope>
5555
</dependency>
5656
<dependency>
5757
<groupId>io.github.sttk</groupId>
5858
<artifactId>reasonedexception</artifactId>
59-
<version>0.3.0</version>
59+
<version>0.3.1</version>
6060
<scope>compile</scope>
6161
</dependency>
6262
<dependency>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
/*
2+
* Copyright (C) 2024 Takayuki Sato. All Rights Reserved.
3+
* This program is free software under MIT License.
4+
* See the file LICENSE in this distribution for more details.
5+
*/
6+
package com.github.sttk.cliargs;
7+
8+
import static com.github.sttk.cliargs.Util.isEmpty;
9+
import static java.util.Collections.emptyList;
10+
11+
import com.github.sttk.linebreak.LineIter;
12+
import com.github.sttk.linebreak.Unicode;
13+
import com.github.sttk.linebreak.Term;
14+
import java.util.Iterator;
15+
import java.util.List;
16+
import java.util.ArrayList;
17+
import java.util.Objects;
18+
19+
/**
20+
* Is the class to print a help text with {@link OptCfg} objects.
21+
* <p>
22+
* This class can output with margins and indentation for each option and text
23+
* block.
24+
*/
25+
public class Help {
26+
27+
private int marginLeft;
28+
private int marginRight;
29+
private List<Block> blocks;
30+
31+
/**
32+
* Is the constructor which create a new instance of {@link Help} class.
33+
* <p>
34+
* This constructor can takes variadic parameters of which the first is
35+
* left margin, and the second is right margin.
36+
*
37+
* @param wrapOpts The variadic parameter of the left margin width and the
38+
* right margin width.
39+
*/
40+
public Help(int ...wrapOpts) {
41+
if (wrapOpts.length > 0) {
42+
this.marginLeft = wrapOpts[0];
43+
}
44+
if (wrapOpts.length > 1) {
45+
this.marginRight = wrapOpts[1];
46+
}
47+
this.blocks = new ArrayList<>();
48+
}
49+
50+
/**
51+
* Creates an iterator to output the help text line by line.
52+
*
53+
* @return An iterator for lines of the help text.
54+
*/
55+
public Iterator<String> iter() {
56+
int lineWidth = Term.getCols();
57+
return new HelpIter(this.blocks, lineWidth);
58+
}
59+
60+
/**
61+
* Registers a text as a text block.
62+
* <p>
63+
* This method can take the variadic parameters of which the first is indent
64+
* width, te second is left margin width, and the third is right margin
65+
* width.
66+
* These margins are added to the margins specified by the constructor.
67+
*
68+
* @param text A text.
69+
* @param wrapOpts The variadic parameter of the indent width, the left
70+
* margin width, and the right margin width.
71+
*/
72+
public void addText(String text, int ...wrapOpts) {
73+
if (text == null) {
74+
text = "";
75+
}
76+
77+
var b = new Block();
78+
b.marginLeft = this.marginLeft;
79+
b.marginRight = this.marginRight;
80+
81+
if (wrapOpts.length > 0) {
82+
b.indent = wrapOpts[0];
83+
}
84+
if (wrapOpts.length > 1) {
85+
b.marginLeft += wrapOpts[1];
86+
}
87+
if (wrapOpts.length > 2) {
88+
b.marginRight += wrapOpts[2];
89+
}
90+
b.texts = new ArrayList<>(1);
91+
b.texts.add(text);
92+
this.blocks.add(b);
93+
}
94+
95+
/**
96+
* Registers multiple texts as a text block.
97+
* <p>
98+
* This method can take the variadic parameters of which the first is indent
99+
* width, te second is left margin width, and the third is right margin
100+
* width.
101+
* These margins are added to the margins specified by the constructor.
102+
*
103+
* @param texts multiple texts.
104+
* @param wrapOpts The variadic parameter of the indent width, the left
105+
* margin width, and the right margin width.
106+
*/
107+
public void addTexts(List<String> texts, int ...wrapOpts) {
108+
if (texts == null) {
109+
texts = emptyList();
110+
}
111+
112+
var b = new Block();
113+
b.marginLeft = this.marginLeft;
114+
b.marginRight = this.marginRight;
115+
116+
if (wrapOpts.length > 0) {
117+
b.indent = wrapOpts[0];
118+
}
119+
if (wrapOpts.length > 1) {
120+
b.marginLeft += wrapOpts[1];
121+
}
122+
if (wrapOpts.length > 2) {
123+
b.marginRight += wrapOpts[2];
124+
}
125+
b.texts = new ArrayList<>(texts);
126+
this.blocks.add(b);
127+
}
128+
129+
/**
130+
* Registers an {@link OptCfg} array and creates the option part in the help
131+
* text as a text block.
132+
* <p>
133+
* This method can take the variadic parameters of which the first is indent
134+
* width, te second is left margin width, and the third is right margin
135+
* width.
136+
* These margins are added to the margins specified by the constructor.
137+
*
138+
* @param optCfgs An {@link OptCfg} array.
139+
* @param wrapOpts The variadic parameter of the indent width, the left
140+
* margin width, and the right margin width.
141+
*/
142+
public void addOpts(OptCfg[] optCfgs, int ...wrapOpts) {
143+
if (optCfgs == null) {
144+
optCfgs = new OptCfg[0];
145+
}
146+
147+
var b = new Block();
148+
b.marginLeft = this.marginLeft;
149+
b.marginRight = this.marginRight;
150+
if (wrapOpts.length > 0) {
151+
b.indent = wrapOpts[0];
152+
}
153+
if (wrapOpts.length > 1) {
154+
b.marginLeft += wrapOpts[1];
155+
}
156+
if (wrapOpts.length > 2) {
157+
b.marginRight += wrapOpts[2];
158+
}
159+
160+
var texts = new ArrayList<String>(optCfgs.length);
161+
162+
final var anyOption = "*";
163+
164+
if (b.indent > 0) {
165+
int i = 0;
166+
for (var cfg : optCfgs) {
167+
var storeKey = cfg.storeKey;
168+
if (Objects.equals(storeKey, anyOption)) {
169+
continue;
170+
}
171+
172+
var text = makeOptTitle(cfg);
173+
var width = Unicode.getTextWidth(text);
174+
if (width + 2 > b.indent) {
175+
text += "\n" + " ".repeat(b.indent) + cfg.desc;
176+
} else {
177+
text += " ".repeat(b.indent - width) + cfg.desc;
178+
}
179+
texts.add(text);
180+
i++;
181+
}
182+
} else {
183+
var widths = new int[optCfgs.length];
184+
int indent = 0;
185+
186+
int i = 0;
187+
for (var cfg : optCfgs) {
188+
var storeKey = cfg.storeKey;
189+
if (storeKey == anyOption) {
190+
continue;
191+
}
192+
193+
String text = makeOptTitle(cfg);
194+
widths[i] = Unicode.getTextWidth(text);
195+
if (indent < widths[i]) {
196+
indent = widths[i];
197+
}
198+
texts.add(text);
199+
i++;
200+
}
201+
indent += 2;
202+
203+
b.indent = indent;
204+
205+
i = 0;
206+
for (var cfg : optCfgs) {
207+
var storeKey = cfg.storeKey;
208+
if (storeKey == anyOption) {
209+
continue;
210+
}
211+
212+
var text = texts.get(i) + " ".repeat(indent - widths[i]) + cfg.desc;
213+
texts.set(i, text);
214+
i++;
215+
}
216+
}
217+
218+
b.texts = new ArrayList<>(texts);
219+
this.blocks.add(b);
220+
}
221+
222+
private String makeOptTitle(OptCfg cfg) {
223+
var title = "";
224+
for (var name : cfg.names) {
225+
title += switch (name.length()) {
226+
case 0 -> "";
227+
case 1 -> ", -" + name;
228+
default -> ", --" + name;
229+
};
230+
}
231+
232+
if (cfg.hasArg && ! isEmpty(cfg.argInHelp)) {
233+
title += " " + cfg.argInHelp;
234+
}
235+
236+
return title.substring(2);
237+
}
238+
239+
/**
240+
* Prints a help text to standard output.
241+
*/
242+
public void print() {
243+
var iter = iter();
244+
245+
while (iter.hasNext()) {
246+
var line = iter.next();
247+
System.out.println(line);
248+
}
249+
}
250+
}
251+
252+
class Block {
253+
int indent;
254+
int marginLeft;
255+
int marginRight;
256+
List<String> texts;
257+
}
258+
259+
class HelpIter implements Iterator<String> {
260+
Iterator<Block> blockIter;
261+
int lineWidth;
262+
263+
Iterator<String> textIter;
264+
int printWidth;
265+
String indent;
266+
String margin;
267+
268+
LineIter lineIter;
269+
270+
HelpIter(List<Block> blocks, int lineWidth) {
271+
if (blocks.isEmpty()) {
272+
var b = new Block();
273+
b.texts = emptyList();
274+
blocks.add(b); // for outputing at least one line.
275+
}
276+
this.blockIter = new ArrayList<>(blocks).iterator();
277+
this.textIter = null;
278+
this.lineIter = null;
279+
this.lineWidth = lineWidth;
280+
}
281+
282+
@Override
283+
public boolean hasNext() {
284+
if (this.lineIter != null && this.lineIter.hasNext()) {
285+
return true;
286+
}
287+
if (this.textIter != null && this.textIter.hasNext()) {
288+
return true;
289+
}
290+
return this.blockIter.hasNext();
291+
}
292+
293+
@Override
294+
public String next() {
295+
if (this.lineIter != null && this.lineIter.hasNext()) {
296+
this.lineIter.setIndent(this.indent);
297+
var line = this.lineIter.next();
298+
return isEmpty(line) ? "" : (this.margin + line);
299+
}
300+
301+
if (this.textIter != null && this.textIter.hasNext()) {
302+
var text = this.textIter.next();
303+
this.lineIter = new LineIter(text, this.printWidth);
304+
if (this.lineIter.hasNext()) {
305+
var line = this.lineIter.next();
306+
return isEmpty(line) ? "" : (this.margin + line);
307+
}
308+
}
309+
310+
if (this.blockIter.hasNext()) {
311+
var b = this.blockIter.next();
312+
313+
this.printWidth = this.lineWidth - b.marginLeft - b.marginRight;
314+
this.indent = " ".repeat(b.indent);
315+
this.margin = " ".repeat(b.marginLeft);
316+
317+
if (this.printWidth <= b.indent) {
318+
this.textIter = null;
319+
return "";
320+
}
321+
this.textIter = b.texts.iterator();
322+
323+
if (this.textIter.hasNext()) {
324+
var text = this.textIter.next();
325+
this.lineIter = new LineIter(text, this.printWidth);
326+
if (this.lineIter.hasNext()) {
327+
var line = this.lineIter.next();
328+
return isEmpty(line) ? "" : (this.margin + line);
329+
}
330+
}
331+
}
332+
333+
return "";
334+
}
335+
}

src/main/java/module-info.java

+1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@
1111
exports com.github.sttk.cliargs;
1212
exports com.github.sttk.cliargs.convert;
1313
requires transitive com.github.sttk.reasonedexception;
14+
requires transitive com.github.sttk.linebreak;
1415
}

0 commit comments

Comments
 (0)