1 module tools.common.getopt;
2 
3 
4 public import std.getopt: getopt, config;
5 import std.getopt;
6 import std.stdio;
7 import std.string;
8 import std.algorithm;
9 
10 
11 void improvedGetoptPrinter(string text, Option[] opt, string footer = null, int width=80){
12 
13 	version(Posix){
14 		import core.sys.posix.sys.ioctl;
15 		static if(__traits(compiles, winsize, winsize.ws_row, TIOCGWINSZ)){
16 			//pragma(msg, "Terminal width detection");
17 			winsize w;
18 			ioctl(stdout.fileno, TIOCGWINSZ, &w);
19 			width = w.ws_col;
20 		}
21 	}
22 
23 	size_t widthOptLong;
24 	bool hasRequiredOpt = false;
25 	size_t widthHelpIndentation;
26 	foreach(ref o ; opt){
27 		if(o.optLong.length > widthOptLong)
28 			widthOptLong = o.optLong.length;
29 		if(o.required)
30 			hasRequiredOpt = true;
31 	}
32 	widthHelpIndentation = widthOptLong + (hasRequiredOpt? 8 : 6);
33 	auto helpIndent = "".leftJustify(widthHelpIndentation);
34 
35 
36 	// Print text
37 	text
38 		.splitLines
39 		.map!(a => a.smartWrap(width, null, " ").splitLines)
40 		.join
41 		.each!((a){
42 			writeln(a);
43 		});
44 	writeln();
45 
46 	if(hasRequiredOpt)
47 		writeln("Options with * are required");
48 
49 	// Print options
50 	foreach(ref o ; opt){
51 		writef(" %s%s %*s  ",
52 			hasRequiredOpt ? (o.required? "* " : "  ") : "",
53 			o.optShort !is null? o.optShort : "  ",
54 			widthOptLong, o.optLong );
55 
56 		bool first = true;
57 		o.help
58 			.splitLines
59 			.map!(a => a.smartWrap(width - widthHelpIndentation).splitLines)
60 			.join
61 			.each!((a){
62 				writeln(first ? "" : helpIndent, a);
63 				first = false;
64 			});
65 	}
66 
67 	// Print footer
68 	if(footer !is null){
69 		writeln();
70 
71 		footer
72 			.splitLines
73 			.map!((a) {
74 				auto l = a.smartWrap(width, null, " ").splitLines;
75 				if(l.length == 0)
76 					l = [""];
77 				return l;
78 			})
79 			.join
80 			.each!((a){
81 				writeln(a);
82 			});
83 	}
84 }
85 
86 
87 private string smartWrap(in string text, size_t width = 80, in string firstindent = null, in string secondindent = null, in size_t tabsize = 8){
88 	import std.uni : isWhite;
89 
90 	return text.splitLines
91 		.map!((ref l){
92 			string indent;
93 			auto indentLen = l.countUntil!(a => !a.isWhite);
94 			if(indentLen > 0)
95 				indent = l[0 .. indentLen];
96 
97 			return l.wrap(width - indent.length, firstindent, secondindent, tabsize)
98 				.splitLines
99 				.map!(a => indent ~ a)
100 				.join("\n");
101 		})
102 		.join("\n");
103 }
104 unittest{
105 	assert("   hello".smartWrap() == "   hello");
106 	assert("   hello world".smartWrap(8) == "   hello\n   world");
107 	assert("\n".smartWrap(8) == "");
108 	assert("\n\n".smartWrap(8) == "\n");
109 	assert("   a\n\n   b".smartWrap(8) == "   a\n\n   b");
110 }
111 
112 
113 
114 template multilineStr(string s){
115 	enum multilineStr = (){
116 		auto lines = s.splitLines();
117 		assert(lines[0].strip == "", "First line must be empty");
118 		assert(lines.length > 1, "Not enough lines");
119 
120 		const tabLen = lines[1].length - lines[1].stripLeft.length;
121 		const tab = lines[1][0 .. tabLen];
122 
123 		return lines[1 .. $]
124 			.map!((l){
125 				if(l.strip.length == 0)
126 					return "";
127 				assert(l[0 .. tabLen] == tab, "Tab mismatch on line '" ~ l ~ "'");
128 				return l[tabLen .. $];
129 			})
130 			.join("\n");
131 	}();
132 }