diff options
| author | 2022-03-30 16:28:16 +0200 | |
|---|---|---|
| committer | 2022-03-30 21:57:51 +0200 | |
| commit | 984c22948b70bd1cbfef9f2d514d42e27541c1f1 (patch) | |
| tree | 3693976dccd1fa8c9ff5d722aba4d306a10c9fdf | |
| parent | Fix long param parsing with '-' and '\_' in name (diff) | |
| download | zig-clap-984c22948b70bd1cbfef9f2d514d42e27541c1f1.tar.gz zig-clap-984c22948b70bd1cbfef9f2d514d42e27541c1f1.tar.xz zig-clap-984c22948b70bd1cbfef9f2d514d42e27541c1f1.zip | |
New `help` api that provides options as to how paramters are printed.
fixes \#28
| -rw-r--r-- | README.md | 12 | ||||
| -rw-r--r-- | clap.zig | 778 | ||||
| -rw-r--r-- | example/README.md.template | 10 | ||||
| -rw-r--r-- | example/help.zig | 2 |
4 files changed, 702 insertions, 100 deletions
| @@ -200,7 +200,8 @@ is generated at runtime. | |||
| 200 | 200 | ||
| 201 | The `help` prints a simple list of all parameters the program can take. It expects the | 201 | The `help` prints a simple list of all parameters the program can take. It expects the |
| 202 | `Id` to have a `description` method and an `value` method so that it can provide that | 202 | `Id` to have a `description` method and an `value` method so that it can provide that |
| 203 | in the output. | 203 | in the output. `HelpOptions` is passed to `help` to control how the help message is |
| 204 | printed. | ||
| 204 | 205 | ||
| 205 | ```zig | 206 | ```zig |
| 206 | const clap = @import("clap"); | 207 | const clap = @import("clap"); |
| @@ -220,15 +221,18 @@ pub fn main() !void { | |||
| 220 | // slice of Param(Help). There is also a helpEx, which can print a | 221 | // slice of Param(Help). There is also a helpEx, which can print a |
| 221 | // help message for any Param, but it is more verbose to call. | 222 | // help message for any Param, but it is more verbose to call. |
| 222 | if (res.args.help) | 223 | if (res.args.help) |
| 223 | return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms); | 224 | return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); |
| 224 | } | 225 | } |
| 225 | 226 | ||
| 226 | ``` | 227 | ``` |
| 227 | 228 | ||
| 228 | ``` | 229 | ``` |
| 229 | $ zig-out/bin/help --help | 230 | $ zig-out/bin/help --help |
| 230 | -h, --help Display this help and exit. | 231 | -h, --help |
| 231 | -v, --version Output version information and exit. | 232 | Display this help and exit. |
| 233 | |||
| 234 | -v, --version | ||
| 235 | Output version information and exit. | ||
| 232 | ``` | 236 | ``` |
| 233 | 237 | ||
| 234 | ### `usage` | 238 | ### `usage` |
| @@ -4,6 +4,7 @@ const builtin = std.builtin; | |||
| 4 | const debug = std.debug; | 4 | const debug = std.debug; |
| 5 | const heap = std.heap; | 5 | const heap = std.heap; |
| 6 | const io = std.io; | 6 | const io = std.io; |
| 7 | const math = std.math; | ||
| 7 | const mem = std.mem; | 8 | const mem = std.mem; |
| 8 | const process = std.process; | 9 | const process = std.process; |
| 9 | const testing = std.testing; | 10 | const testing = std.testing; |
| @@ -81,10 +82,17 @@ pub fn Param(comptime Id: type) type { | |||
| 81 | /// Takes a string and parses it into many Param(Help). Returned is a newly allocated slice | 82 | /// Takes a string and parses it into many Param(Help). Returned is a newly allocated slice |
| 82 | /// containing all the parsed params. The caller is responsible for freeing the slice. | 83 | /// containing all the parsed params. The caller is responsible for freeing the slice. |
| 83 | pub fn parseParams(allocator: mem.Allocator, str: []const u8) ![]Param(Help) { | 84 | pub fn parseParams(allocator: mem.Allocator, str: []const u8) ![]Param(Help) { |
| 85 | var end: usize = undefined; | ||
| 86 | return parseParamsEx(allocator, str, &end); | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Takes a string and parses it into many Param(Help). Returned is a newly allocated slice | ||
| 90 | /// containing all the parsed params. The caller is responsible for freeing the slice. | ||
| 91 | pub fn parseParamsEx(allocator: mem.Allocator, str: []const u8, end: *usize) ![]Param(Help) { | ||
| 84 | var list = std.ArrayList(Param(Help)).init(allocator); | 92 | var list = std.ArrayList(Param(Help)).init(allocator); |
| 85 | errdefer list.deinit(); | 93 | errdefer list.deinit(); |
| 86 | 94 | ||
| 87 | try parseParamsIntoArrayList(&list, str); | 95 | try parseParamsIntoArrayListEx(&list, str, end); |
| 88 | return list.toOwnedSlice(); | 96 | return list.toOwnedSlice(); |
| 89 | } | 97 | } |
| 90 | 98 | ||
| @@ -92,8 +100,16 @@ pub fn parseParams(allocator: mem.Allocator, str: []const u8) ![]Param(Help) { | |||
| 92 | /// exactly the number of params that was parsed from `str`. A parse error becomes a compiler | 100 | /// exactly the number of params that was parsed from `str`. A parse error becomes a compiler |
| 93 | /// error. | 101 | /// error. |
| 94 | pub fn parseParamsComptime(comptime str: []const u8) [countParams(str)]Param(Help) { | 102 | pub fn parseParamsComptime(comptime str: []const u8) [countParams(str)]Param(Help) { |
| 103 | var end: usize = undefined; | ||
| 95 | var res: [countParams(str)]Param(Help) = undefined; | 104 | var res: [countParams(str)]Param(Help) = undefined; |
| 96 | _ = parseParamsIntoSlice(&res, str) catch unreachable; | 105 | _ = parseParamsIntoSliceEx(&res, str, &end) catch { |
| 106 | const loc = std.zig.findLineColumn(str, end); | ||
| 107 | @compileError(std.fmt.comptimePrint("error:{}:{}: Failed to parse parameter:\n{s}", .{ | ||
| 108 | loc.line + 1, | ||
| 109 | loc.column + 1, | ||
| 110 | loc.source_line, | ||
| 111 | })); | ||
| 112 | }; | ||
| 97 | return res; | 113 | return res; |
| 98 | } | 114 | } |
| 99 | 115 | ||
| @@ -131,14 +147,39 @@ pub fn parseParamsIntoSlice(slice: []Param(Help), str: []const u8) ![]Param(Help | |||
| 131 | return list.items; | 147 | return list.items; |
| 132 | } | 148 | } |
| 133 | 149 | ||
| 150 | /// Takes a string and parses it into many Param(Help), which are written to `slice`. A subslice | ||
| 151 | /// is returned, containing all the parameters parsed. This function will fail if the input slice | ||
| 152 | /// is to small. | ||
| 153 | pub fn parseParamsIntoSliceEx(slice: []Param(Help), str: []const u8, end: *usize) ![]Param(Help) { | ||
| 154 | var null_alloc = heap.FixedBufferAllocator.init(""); | ||
| 155 | var list = std.ArrayList(Param(Help)){ | ||
| 156 | .allocator = null_alloc.allocator(), | ||
| 157 | .items = slice[0..0], | ||
| 158 | .capacity = slice.len, | ||
| 159 | }; | ||
| 160 | |||
| 161 | try parseParamsIntoArrayListEx(&list, str, end); | ||
| 162 | return list.items; | ||
| 163 | } | ||
| 164 | |||
| 134 | /// Takes a string and parses it into many Param(Help), which are appended onto `list`. | 165 | /// Takes a string and parses it into many Param(Help), which are appended onto `list`. |
| 135 | pub fn parseParamsIntoArrayList(list: *std.ArrayList(Param(Help)), str: []const u8) !void { | 166 | pub fn parseParamsIntoArrayList(list: *std.ArrayList(Param(Help)), str: []const u8) !void { |
| 167 | var end: usize = undefined; | ||
| 168 | return parseParamsIntoArrayListEx(list, str, &end); | ||
| 169 | } | ||
| 170 | |||
| 171 | /// Takes a string and parses it into many Param(Help), which are appended onto `list`. | ||
| 172 | pub fn parseParamsIntoArrayListEx(list: *std.ArrayList(Param(Help)), str: []const u8, end: *usize) !void { | ||
| 136 | var i: usize = 0; | 173 | var i: usize = 0; |
| 137 | while (i != str.len) { | 174 | while (i != str.len) { |
| 138 | var end: usize = undefined; | 175 | var end_of_this: usize = undefined; |
| 139 | try list.append(try parseParamEx(str[i..], &end)); | 176 | errdefer end.* = i + end_of_this; |
| 140 | i += end; | 177 | |
| 178 | try list.append(try parseParamEx(str[i..], &end_of_this)); | ||
| 179 | i += end_of_this; | ||
| 141 | } | 180 | } |
| 181 | |||
| 182 | end.* = str.len; | ||
| 142 | } | 183 | } |
| 143 | 184 | ||
| 144 | pub fn parseParam(str: []const u8) !Param(Help) { | 185 | pub fn parseParam(str: []const u8) !Param(Help) { |
| @@ -182,8 +223,6 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) { | |||
| 182 | third_dot_of_multi_value, | 223 | third_dot_of_multi_value, |
| 183 | 224 | ||
| 184 | before_description, | 225 | before_description, |
| 185 | before_description_new_line, | ||
| 186 | |||
| 187 | rest_of_description, | 226 | rest_of_description, |
| 188 | rest_of_description_new_line, | 227 | rest_of_description_new_line, |
| 189 | } = .start; | 228 | } = .start; |
| @@ -208,7 +247,11 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) { | |||
| 208 | }, | 247 | }, |
| 209 | .end_of_short_name => switch (c) { | 248 | .end_of_short_name => switch (c) { |
| 210 | ' ', '\t' => state = .before_long_name_or_value_or_description, | 249 | ' ', '\t' => state = .before_long_name_or_value_or_description, |
| 211 | '\n' => state = .before_description_new_line, | 250 | '\n' => { |
| 251 | start = i + 1; | ||
| 252 | end.* = i + 1; | ||
| 253 | state = .rest_of_description_new_line; | ||
| 254 | }, | ||
| 212 | ',' => state = .before_long_name, | 255 | ',' => state = .before_long_name, |
| 213 | else => return error.InvalidParameter, | 256 | else => return error.InvalidParameter, |
| 214 | }, | 257 | }, |
| @@ -237,7 +280,9 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) { | |||
| 237 | }, | 280 | }, |
| 238 | '\n' => { | 281 | '\n' => { |
| 239 | res.names.long = str[start..i]; | 282 | res.names.long = str[start..i]; |
| 240 | state = .before_description_new_line; | 283 | start = i + 1; |
| 284 | end.* = i + 1; | ||
| 285 | state = .rest_of_description_new_line; | ||
| 241 | }, | 286 | }, |
| 242 | else => return error.InvalidParameter, | 287 | else => return error.InvalidParameter, |
| 243 | }, | 288 | }, |
| @@ -278,7 +323,11 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) { | |||
| 278 | .end_of_one_value => switch (c) { | 323 | .end_of_one_value => switch (c) { |
| 279 | '.' => state = .second_dot_of_multi_value, | 324 | '.' => state = .second_dot_of_multi_value, |
| 280 | ' ', '\t' => state = .before_description, | 325 | ' ', '\t' => state = .before_description, |
| 281 | '\n' => state = .before_description_new_line, | 326 | '\n' => { |
| 327 | start = i + 1; | ||
| 328 | end.* = i + 1; | ||
| 329 | state = .rest_of_description_new_line; | ||
| 330 | }, | ||
| 282 | else => { | 331 | else => { |
| 283 | start = i; | 332 | start = i; |
| 284 | state = .rest_of_description; | 333 | state = .rest_of_description; |
| @@ -298,17 +347,10 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) { | |||
| 298 | 347 | ||
| 299 | .before_description => switch (c) { | 348 | .before_description => switch (c) { |
| 300 | ' ', '\t' => {}, | 349 | ' ', '\t' => {}, |
| 301 | '\n' => state = .before_description_new_line, | 350 | '\n' => { |
| 302 | else => { | 351 | start = i + 1; |
| 303 | start = i; | 352 | end.* = i + 1; |
| 304 | state = .rest_of_description; | 353 | state = .rest_of_description_new_line; |
| 305 | }, | ||
| 306 | }, | ||
| 307 | .before_description_new_line => switch (c) { | ||
| 308 | ' ', '\t', '\n' => {}, | ||
| 309 | '-', '<' => { | ||
| 310 | end.* = i; | ||
| 311 | break; | ||
| 312 | }, | 354 | }, |
| 313 | else => { | 355 | else => { |
| 314 | start = i; | 356 | start = i; |
| @@ -316,13 +358,16 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) { | |||
| 316 | }, | 358 | }, |
| 317 | }, | 359 | }, |
| 318 | .rest_of_description => switch (c) { | 360 | .rest_of_description => switch (c) { |
| 319 | '\n' => state = .rest_of_description_new_line, | 361 | '\n' => { |
| 362 | end.* = i; | ||
| 363 | state = .rest_of_description_new_line; | ||
| 364 | }, | ||
| 320 | else => {}, | 365 | else => {}, |
| 321 | }, | 366 | }, |
| 322 | .rest_of_description_new_line => switch (c) { | 367 | .rest_of_description_new_line => switch (c) { |
| 323 | ' ', '\t', '\n' => {}, | 368 | ' ', '\t', '\n' => {}, |
| 324 | '-', '<' => { | 369 | '-', '<' => { |
| 325 | res.id.desc = mem.trimRight(u8, str[start..i], " \t\n\r"); | 370 | res.id.desc = str[start..end.*]; |
| 326 | end.* = i; | 371 | end.* = i; |
| 327 | break; | 372 | break; |
| 328 | }, | 373 | }, |
| @@ -330,17 +375,15 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) { | |||
| 330 | }, | 375 | }, |
| 331 | } | 376 | } |
| 332 | } else { | 377 | } else { |
| 333 | end.* = str.len; | 378 | defer end.* = str.len; |
| 334 | switch (state) { | 379 | switch (state) { |
| 335 | .rest_of_description, .rest_of_description_new_line => { | 380 | .rest_of_description => res.id.desc = str[start..], |
| 336 | res.id.desc = mem.trimRight(u8, str[start..], " \t\n\r"); | 381 | .rest_of_description_new_line => res.id.desc = str[start..end.*], |
| 337 | }, | ||
| 338 | .rest_of_long_name => res.names.long = str[start..], | 382 | .rest_of_long_name => res.names.long = str[start..], |
| 339 | .end_of_short_name, | 383 | .end_of_short_name, |
| 340 | .end_of_one_value, | 384 | .end_of_one_value, |
| 341 | .before_value_or_description, | 385 | .before_value_or_description, |
| 342 | .before_description, | 386 | .before_description, |
| 343 | .before_description_new_line, | ||
| 344 | => {}, | 387 | => {}, |
| 345 | else => return error.InvalidParameter, | 388 | else => return error.InvalidParameter, |
| 346 | } | 389 | } |
| @@ -350,7 +393,16 @@ pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) { | |||
| 350 | } | 393 | } |
| 351 | 394 | ||
| 352 | fn testParseParams(str: []const u8, expected_params: []const Param(Help)) !void { | 395 | fn testParseParams(str: []const u8, expected_params: []const Param(Help)) !void { |
| 353 | const actual_params = try parseParams(testing.allocator, str); | 396 | var end: usize = undefined; |
| 397 | const actual_params = parseParamsEx(testing.allocator, str, &end) catch |err| { | ||
| 398 | const loc = std.zig.findLineColumn(str, end); | ||
| 399 | std.debug.print("error:{}:{}: Failed to parse parameter:\n{s}\n", .{ | ||
| 400 | loc.line + 1, | ||
| 401 | loc.column + 1, | ||
| 402 | loc.source_line, | ||
| 403 | }); | ||
| 404 | return err; | ||
| 405 | }; | ||
| 354 | defer testing.allocator.free(actual_params); | 406 | defer testing.allocator.free(actual_params); |
| 355 | 407 | ||
| 356 | try testing.expectEqual(expected_params.len, actual_params.len); | 408 | try testing.expectEqual(expected_params.len, actual_params.len); |
| @@ -389,10 +441,10 @@ test "parseParams" { | |||
| 389 | \\--long <A | B> Help text | 441 | \\--long <A | B> Help text |
| 390 | \\<A> Help text | 442 | \\<A> Help text |
| 391 | \\<A>... Help text | 443 | \\<A>... Help text |
| 392 | \\--aa This is | 444 | \\--aa |
| 445 | \\ This is | ||
| 393 | \\ help spanning multiple | 446 | \\ help spanning multiple |
| 394 | \\ lines | 447 | \\ lines |
| 395 | \\ | ||
| 396 | \\--aa This msg should end and the newline cause of new param | 448 | \\--aa This msg should end and the newline cause of new param |
| 397 | \\--bb This should be a new param | 449 | \\--bb This should be a new param |
| 398 | \\ | 450 | \\ |
| @@ -461,7 +513,7 @@ test "parseParams" { | |||
| 461 | .{ | 513 | .{ |
| 462 | .id = .{ | 514 | .id = .{ |
| 463 | .desc = | 515 | .desc = |
| 464 | \\This is | 516 | \\ This is |
| 465 | \\ help spanning multiple | 517 | \\ help spanning multiple |
| 466 | \\ lines | 518 | \\ lines |
| 467 | , | 519 | , |
| @@ -969,13 +1021,71 @@ pub const Help = struct { | |||
| 969 | } | 1021 | } |
| 970 | }; | 1022 | }; |
| 971 | 1023 | ||
| 972 | /// Will print a help message in the following format: | 1024 | pub const HelpOptions = struct { |
| 973 | /// -s, --long <valueText> helpText | 1025 | /// Render the description of a parameter in a simular way to how markdown would render |
| 974 | /// -s, helpText | 1026 | /// such a string. This means that single newlines wont be respected unless followed by |
| 975 | /// -s <valueText> helpText | 1027 | /// bullet points or other markdown elements. |
| 976 | /// --long helpText | 1028 | markdown_lite: bool = true, |
| 977 | /// --long <valueText> helpText | 1029 | |
| 978 | pub fn help(stream: anytype, comptime Id: type, params: []const Param(Id)) !void { | 1030 | /// Wether `help` should print the description of a parameter on a new line instead of after |
| 1031 | /// the parameter names. This options works together with `description_indent` to change | ||
| 1032 | /// where descriptions are printed. | ||
| 1033 | /// | ||
| 1034 | /// description_on_new_line=false, description_indent=4 | ||
| 1035 | /// | ||
| 1036 | /// -a, --aa <v> This is a description | ||
| 1037 | /// that is not placed on | ||
| 1038 | /// a new line. | ||
| 1039 | /// | ||
| 1040 | /// description_on_new_line=true, description_indent=4 | ||
| 1041 | /// | ||
| 1042 | /// -a, --aa <v> | ||
| 1043 | /// This is a description | ||
| 1044 | /// that is placed on a | ||
| 1045 | /// new line. | ||
| 1046 | description_on_new_line: bool = true, | ||
| 1047 | |||
| 1048 | /// How much to indent descriptions. See `description_on_new_line` for examples of how this | ||
| 1049 | /// changes the output. | ||
| 1050 | description_indent: usize = 8, | ||
| 1051 | |||
| 1052 | /// How much to indent each paramter. | ||
| 1053 | /// | ||
| 1054 | /// indent=0, description_on_new_line=false, description_indent=4 | ||
| 1055 | /// | ||
| 1056 | /// -a, --aa <v> This is a description | ||
| 1057 | /// that is not placed on | ||
| 1058 | /// a new line. | ||
| 1059 | /// | ||
| 1060 | /// indent=4, description_on_new_line=false, description_indent=4 | ||
| 1061 | /// | ||
| 1062 | /// -a, --aa <v> This is a description | ||
| 1063 | /// that is not placed on | ||
| 1064 | /// a new line. | ||
| 1065 | /// | ||
| 1066 | indent: usize = 4, | ||
| 1067 | |||
| 1068 | /// The maximum width of the help message. `help` will try to break the description of | ||
| 1069 | /// paramters into multiple lines if they exeed this maximum. Setting this to the width | ||
| 1070 | /// of the terminal is a nice way of using this option. | ||
| 1071 | max_width: usize = std.math.maxInt(usize), | ||
| 1072 | |||
| 1073 | /// The number of empty lines between each printed parameter. | ||
| 1074 | spacing_between_parameters: usize = 1, | ||
| 1075 | }; | ||
| 1076 | |||
| 1077 | /// Print a slice of `Param` formatted as a help string to `writer`. This function expects | ||
| 1078 | /// `Id` to have the methods `description` and `value` which are used by `help` to describe | ||
| 1079 | /// each parameter. Using `Help` as `Id` is good choice. | ||
| 1080 | /// | ||
| 1081 | /// The output can be constumized with the `opt` parameter. For default formatting `.{}` can | ||
| 1082 | /// be passed. | ||
| 1083 | pub fn help( | ||
| 1084 | writer: anytype, | ||
| 1085 | comptime Id: type, | ||
| 1086 | params: []const Param(Id), | ||
| 1087 | opt: HelpOptions, | ||
| 1088 | ) !void { | ||
| 979 | const max_spacing = blk: { | 1089 | const max_spacing = blk: { |
| 980 | var res: usize = 0; | 1090 | var res: usize = 0; |
| 981 | for (params) |param| { | 1091 | for (params) |param| { |
| @@ -988,48 +1098,186 @@ pub fn help(stream: anytype, comptime Id: type, params: []const Param(Id)) !void | |||
| 988 | break :blk res; | 1098 | break :blk res; |
| 989 | }; | 1099 | }; |
| 990 | 1100 | ||
| 1101 | const description_indentation = opt.indent + | ||
| 1102 | opt.description_indent + | ||
| 1103 | max_spacing * @boolToInt(!opt.description_on_new_line); | ||
| 1104 | |||
| 1105 | var first_paramter: bool = true; | ||
| 991 | for (params) |param| { | 1106 | for (params) |param| { |
| 992 | if (param.names.short == null and param.names.long == null) | 1107 | if (param.names.longest().kind == .positinal) |
| 993 | continue; | 1108 | continue; |
| 1109 | if (!first_paramter) | ||
| 1110 | try writer.writeByteNTimes('\n', opt.spacing_between_parameters); | ||
| 1111 | |||
| 1112 | first_paramter = false; | ||
| 1113 | try writer.writeByteNTimes(' ', opt.indent); | ||
| 994 | 1114 | ||
| 995 | var cs = io.countingWriter(stream); | 1115 | var cw = io.countingWriter(writer); |
| 996 | try stream.writeAll("\t"); | 1116 | try printParam(cw.writer(), Id, param); |
| 997 | try printParam(cs.writer(), Id, param); | 1117 | |
| 998 | try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, cs.bytes_written)); | 1118 | const Writer = DescriptionWriter(@TypeOf(writer)); |
| 1119 | var description_writer = Writer{ | ||
| 1120 | .underlying_writer = writer, | ||
| 1121 | .indentation = description_indentation, | ||
| 1122 | .printed_chars = @intCast(usize, cw.bytes_written), | ||
| 1123 | .max_width = opt.max_width, | ||
| 1124 | }; | ||
| 1125 | |||
| 1126 | if (opt.description_on_new_line) | ||
| 1127 | try description_writer.newline(); | ||
| 1128 | |||
| 1129 | const min_description_indent = blk: { | ||
| 1130 | const description = param.id.description(); | ||
| 1131 | |||
| 1132 | var first_line = true; | ||
| 1133 | var res: usize = std.math.maxInt(usize); | ||
| 1134 | var it = mem.tokenize(u8, description, "\n"); | ||
| 1135 | while (it.next()) |line| : (first_line = false) { | ||
| 1136 | const trimmed = mem.trimLeft(u8, line, " "); | ||
| 1137 | const indent = line.len - trimmed.len; | ||
| 1138 | |||
| 1139 | // If the first line has no indentation, then we ignore the indentation of the | ||
| 1140 | // first line. We do this as the parameter might have been parsed from: | ||
| 1141 | // | ||
| 1142 | // -a, --aa The first line | ||
| 1143 | // is not indented, | ||
| 1144 | // but the rest of | ||
| 1145 | // the lines are. | ||
| 1146 | // | ||
| 1147 | // In this case, we want to pretend that the first line has the same indentation | ||
| 1148 | // as the min_description_indent, even though it is not so in the string we get. | ||
| 1149 | if (first_line and indent == 0) | ||
| 1150 | continue; | ||
| 1151 | if (indent < res) | ||
| 1152 | res = indent; | ||
| 1153 | } | ||
| 1154 | |||
| 1155 | break :blk res; | ||
| 1156 | }; | ||
| 999 | 1157 | ||
| 1000 | const description = param.id.description(); | 1158 | const description = param.id.description(); |
| 1001 | var it = mem.split(u8, description, "\n"); | 1159 | var it = mem.split(u8, description, "\n"); |
| 1002 | var indent_line = false; | 1160 | var first_line = true; |
| 1003 | while (it.next()) |line| : (indent_line = true) { | 1161 | var non_emitted_newlines: usize = 0; |
| 1004 | if (indent_line) { | 1162 | var last_line_indentation: usize = 0; |
| 1005 | try stream.writeAll("\t"); | 1163 | while (it.next()) |raw_line| : (first_line = false) { |
| 1006 | try stream.writeByteNTimes(' ', max_spacing); | 1164 | // First line might be special. See comment above. |
| 1165 | const indented_line = if (first_line and !mem.startsWith(u8, raw_line, " ")) | ||
| 1166 | raw_line | ||
| 1167 | else | ||
| 1168 | raw_line[math.min(min_description_indent, raw_line.len)..]; | ||
| 1169 | |||
| 1170 | const line = mem.trimLeft(u8, indented_line, " "); | ||
| 1171 | if (line.len == 0) { | ||
| 1172 | non_emitted_newlines += 1; | ||
| 1173 | continue; | ||
| 1007 | } | 1174 | } |
| 1008 | try stream.writeAll("\t"); | 1175 | |
| 1009 | try stream.writeAll(mem.trimLeft(u8, line, " \t")); | 1176 | const line_indentation = indented_line.len - line.len; |
| 1010 | try stream.writeAll("\n"); | 1177 | description_writer.indentation = description_indentation + line_indentation; |
| 1178 | |||
| 1179 | if (opt.markdown_lite) { | ||
| 1180 | const new_paragraph = non_emitted_newlines > 1; | ||
| 1181 | |||
| 1182 | const does_not_have_same_indent_as_last_line = | ||
| 1183 | line_indentation != last_line_indentation; | ||
| 1184 | |||
| 1185 | const starts_with_control_char = mem.indexOfScalar(u8, "=*", line[0]) != null; | ||
| 1186 | |||
| 1187 | // Either the input contains 2 or more newlines, in which case we should start | ||
| 1188 | // a new paragraph. | ||
| 1189 | if (new_paragraph) { | ||
| 1190 | try description_writer.newline(); | ||
| 1191 | try description_writer.newline(); | ||
| 1192 | } | ||
| 1193 | // Or this line has a special control char or different indentation which means | ||
| 1194 | // we should output it on a new line as well. | ||
| 1195 | else if (starts_with_control_char or does_not_have_same_indent_as_last_line) { | ||
| 1196 | try description_writer.newline(); | ||
| 1197 | } | ||
| 1198 | } else { | ||
| 1199 | // For none markdown like format, we just respect the newlines in the input | ||
| 1200 | // string and output them as is. | ||
| 1201 | var i: usize = 0; | ||
| 1202 | while (i < non_emitted_newlines) : (i += 1) | ||
| 1203 | try description_writer.newline(); | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | var words = mem.tokenize(u8, line, " "); | ||
| 1207 | while (words.next()) |word| | ||
| 1208 | try description_writer.writeWord(word); | ||
| 1209 | |||
| 1210 | // We have not emitted the end of this line yet. | ||
| 1211 | non_emitted_newlines = 1; | ||
| 1212 | last_line_indentation = line_indentation; | ||
| 1011 | } | 1213 | } |
| 1214 | |||
| 1215 | try writer.writeAll("\n"); | ||
| 1012 | } | 1216 | } |
| 1013 | } | 1217 | } |
| 1014 | 1218 | ||
| 1219 | fn DescriptionWriter(comptime UnderlyingWriter: type) type { | ||
| 1220 | return struct { | ||
| 1221 | pub const WriteError = UnderlyingWriter.Error; | ||
| 1222 | |||
| 1223 | underlying_writer: UnderlyingWriter, | ||
| 1224 | |||
| 1225 | indentation: usize, | ||
| 1226 | max_width: usize, | ||
| 1227 | printed_chars: usize, | ||
| 1228 | |||
| 1229 | pub fn writeWord(writer: *@This(), word: []const u8) !void { | ||
| 1230 | debug.assert(word.len != 0); | ||
| 1231 | |||
| 1232 | var first_word = writer.printed_chars <= writer.indentation; | ||
| 1233 | const chars_to_write = word.len + @boolToInt(!first_word); | ||
| 1234 | if (chars_to_write + writer.printed_chars > writer.max_width) { | ||
| 1235 | // If the word does not fit on this line, then we insert a new line and print | ||
| 1236 | // it on that line. The only exception to this is if this was the first word. | ||
| 1237 | // If the first word does not fit on this line, then it will also not fit on the | ||
| 1238 | // next one. In that case, all we can really do is just output the word. | ||
| 1239 | if (!first_word) | ||
| 1240 | try writer.newline(); | ||
| 1241 | |||
| 1242 | first_word = true; | ||
| 1243 | } | ||
| 1244 | |||
| 1245 | if (!first_word) | ||
| 1246 | try writer.underlying_writer.writeAll(" "); | ||
| 1247 | |||
| 1248 | try writer.ensureIndented(); | ||
| 1249 | try writer.underlying_writer.writeAll(word); | ||
| 1250 | writer.printed_chars += chars_to_write; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | pub fn newline(writer: *@This()) !void { | ||
| 1254 | try writer.underlying_writer.writeAll("\n"); | ||
| 1255 | writer.printed_chars = 0; | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | fn ensureIndented(writer: *@This()) !void { | ||
| 1259 | if (writer.printed_chars < writer.indentation) { | ||
| 1260 | const to_indent = writer.indentation - writer.printed_chars; | ||
| 1261 | try writer.underlying_writer.writeByteNTimes(' ', to_indent); | ||
| 1262 | writer.printed_chars += to_indent; | ||
| 1263 | } | ||
| 1264 | } | ||
| 1265 | }; | ||
| 1266 | } | ||
| 1267 | |||
| 1015 | fn printParam( | 1268 | fn printParam( |
| 1016 | stream: anytype, | 1269 | stream: anytype, |
| 1017 | comptime Id: type, | 1270 | comptime Id: type, |
| 1018 | param: Param(Id), | 1271 | param: Param(Id), |
| 1019 | ) !void { | 1272 | ) !void { |
| 1020 | if (param.names.short) |s| { | 1273 | try stream.writeAll(&[_]u8{ |
| 1021 | try stream.writeAll(&[_]u8{ '-', s }); | 1274 | if (param.names.short) |_| '-' else ' ', |
| 1022 | } else { | 1275 | param.names.short orelse ' ', |
| 1023 | try stream.writeAll(" "); | 1276 | }); |
| 1024 | } | ||
| 1025 | if (param.names.long) |l| { | ||
| 1026 | if (param.names.short) |_| { | ||
| 1027 | try stream.writeAll(", "); | ||
| 1028 | } else { | ||
| 1029 | try stream.writeAll(" "); | ||
| 1030 | } | ||
| 1031 | 1277 | ||
| 1032 | try stream.writeAll("--"); | 1278 | if (param.names.long) |l| { |
| 1279 | try stream.writeByte(if (param.names.short) |_| ',' else ' '); | ||
| 1280 | try stream.writeAll(" --"); | ||
| 1033 | try stream.writeAll(l); | 1281 | try stream.writeAll(l); |
| 1034 | } | 1282 | } |
| 1035 | 1283 | ||
| @@ -1043,39 +1291,386 @@ fn printParam( | |||
| 1043 | try stream.writeAll("..."); | 1291 | try stream.writeAll("..."); |
| 1044 | } | 1292 | } |
| 1045 | 1293 | ||
| 1294 | fn testHelp(opt: HelpOptions, str: []const u8) !void { | ||
| 1295 | const params = try parseParams(testing.allocator, str); | ||
| 1296 | defer testing.allocator.free(params); | ||
| 1297 | |||
| 1298 | var buf: [2048]u8 = undefined; | ||
| 1299 | var fbs = io.fixedBufferStream(&buf); | ||
| 1300 | try help(fbs.writer(), Help, params, opt); | ||
| 1301 | try testing.expectEqualStrings(str, fbs.getWritten()); | ||
| 1302 | } | ||
| 1303 | |||
| 1046 | test "clap.help" { | 1304 | test "clap.help" { |
| 1047 | var buf: [1024]u8 = undefined; | 1305 | try testHelp(.{}, |
| 1048 | var slice_stream = io.fixedBufferStream(&buf); | 1306 | \\ -a |
| 1307 | \\ Short flag. | ||
| 1308 | \\ | ||
| 1309 | \\ -b <V1> | ||
| 1310 | \\ Short option. | ||
| 1311 | \\ | ||
| 1312 | \\ --aa | ||
| 1313 | \\ Long flag. | ||
| 1314 | \\ | ||
| 1315 | \\ --bb <V2> | ||
| 1316 | \\ Long option. | ||
| 1317 | \\ | ||
| 1318 | \\ -c, --cc | ||
| 1319 | \\ Both flag. | ||
| 1320 | \\ | ||
| 1321 | \\ --complicate | ||
| 1322 | \\ Flag with a complicated and very long description that spans multiple lines. | ||
| 1323 | \\ | ||
| 1324 | \\ Paragraph number 2: | ||
| 1325 | \\ * Bullet point | ||
| 1326 | \\ * Bullet point | ||
| 1327 | \\ | ||
| 1328 | \\ Example: | ||
| 1329 | \\ something something something | ||
| 1330 | \\ | ||
| 1331 | \\ -d, --dd <V3> | ||
| 1332 | \\ Both option. | ||
| 1333 | \\ | ||
| 1334 | \\ -d, --dd <V3>... | ||
| 1335 | \\ Both repeated option. | ||
| 1336 | \\ | ||
| 1337 | ); | ||
| 1049 | 1338 | ||
| 1050 | const params = comptime parseParamsComptime( | 1339 | try testHelp(.{ .markdown_lite = false }, |
| 1051 | \\-a Short flag. | 1340 | \\ -a |
| 1052 | \\-b <V1> Short option. | 1341 | \\ Short flag. |
| 1053 | \\--aa Long flag. | 1342 | \\ |
| 1054 | \\--bb <V2> Long option. | 1343 | \\ -b <V1> |
| 1055 | \\-c, --cc Both flag. | 1344 | \\ Short option. |
| 1056 | \\--complicate Flag with a complicated and | 1345 | \\ |
| 1057 | \\ very long description that | 1346 | \\ --aa |
| 1058 | \\ spans multiple lines. | 1347 | \\ Long flag. |
| 1059 | \\-d, --dd <V3> Both option. | 1348 | \\ |
| 1060 | \\-d, --dd <V3>... Both repeated option. | 1349 | \\ --bb <V2> |
| 1061 | \\<P> Positional. This should not appear in the help message. | 1350 | \\ Long option. |
| 1351 | \\ | ||
| 1352 | \\ -c, --cc | ||
| 1353 | \\ Both flag. | ||
| 1354 | \\ | ||
| 1355 | \\ --complicate | ||
| 1356 | \\ Flag with a complicated and | ||
| 1357 | \\ very long description that | ||
| 1358 | \\ spans multiple lines. | ||
| 1359 | \\ | ||
| 1360 | \\ Paragraph number 2: | ||
| 1361 | \\ * Bullet point | ||
| 1362 | \\ * Bullet point | ||
| 1363 | \\ | ||
| 1364 | \\ | ||
| 1365 | \\ Example: | ||
| 1366 | \\ something something something | ||
| 1367 | \\ | ||
| 1368 | \\ -d, --dd <V3> | ||
| 1369 | \\ Both option. | ||
| 1370 | \\ | ||
| 1371 | \\ -d, --dd <V3>... | ||
| 1372 | \\ Both repeated option. | ||
| 1062 | \\ | 1373 | \\ |
| 1063 | ); | 1374 | ); |
| 1064 | 1375 | ||
| 1065 | try help(slice_stream.writer(), Help, ¶ms); | 1376 | try testHelp(.{ .indent = 0 }, |
| 1066 | const expected = "" ++ | 1377 | \\-a |
| 1067 | "\t-a \tShort flag.\n" ++ | 1378 | \\ Short flag. |
| 1068 | "\t-b <V1> \tShort option.\n" ++ | 1379 | \\ |
| 1069 | "\t --aa \tLong flag.\n" ++ | 1380 | \\-b <V1> |
| 1070 | "\t --bb <V2> \tLong option.\n" ++ | 1381 | \\ Short option. |
| 1071 | "\t-c, --cc \tBoth flag.\n" ++ | 1382 | \\ |
| 1072 | "\t --complicate\tFlag with a complicated and\n" ++ | 1383 | \\ --aa |
| 1073 | "\t \tvery long description that\n" ++ | 1384 | \\ Long flag. |
| 1074 | "\t \tspans multiple lines.\n" ++ | 1385 | \\ |
| 1075 | "\t-d, --dd <V3> \tBoth option.\n" ++ | 1386 | \\ --bb <V2> |
| 1076 | "\t-d, --dd <V3>...\tBoth repeated option.\n"; | 1387 | \\ Long option. |
| 1388 | \\ | ||
| 1389 | \\-c, --cc | ||
| 1390 | \\ Both flag. | ||
| 1391 | \\ | ||
| 1392 | \\ --complicate | ||
| 1393 | \\ Flag with a complicated and very long description that spans multiple lines. | ||
| 1394 | \\ | ||
| 1395 | \\ Paragraph number 2: | ||
| 1396 | \\ * Bullet point | ||
| 1397 | \\ * Bullet point | ||
| 1398 | \\ | ||
| 1399 | \\ Example: | ||
| 1400 | \\ something something something | ||
| 1401 | \\ | ||
| 1402 | \\-d, --dd <V3> | ||
| 1403 | \\ Both option. | ||
| 1404 | \\ | ||
| 1405 | \\-d, --dd <V3>... | ||
| 1406 | \\ Both repeated option. | ||
| 1407 | \\ | ||
| 1408 | ); | ||
| 1077 | 1409 | ||
| 1078 | try testing.expectEqualStrings(expected, slice_stream.getWritten()); | 1410 | try testHelp(.{ .indent = 0 }, |
| 1411 | \\-a | ||
| 1412 | \\ Short flag. | ||
| 1413 | \\ | ||
| 1414 | \\-b <V1> | ||
| 1415 | \\ Short option. | ||
| 1416 | \\ | ||
| 1417 | \\ --aa | ||
| 1418 | \\ Long flag. | ||
| 1419 | \\ | ||
| 1420 | \\ --bb <V2> | ||
| 1421 | \\ Long option. | ||
| 1422 | \\ | ||
| 1423 | \\-c, --cc | ||
| 1424 | \\ Both flag. | ||
| 1425 | \\ | ||
| 1426 | \\ --complicate | ||
| 1427 | \\ Flag with a complicated and very long description that spans multiple lines. | ||
| 1428 | \\ | ||
| 1429 | \\ Paragraph number 2: | ||
| 1430 | \\ * Bullet point | ||
| 1431 | \\ * Bullet point | ||
| 1432 | \\ | ||
| 1433 | \\ Example: | ||
| 1434 | \\ something something something | ||
| 1435 | \\ | ||
| 1436 | \\-d, --dd <V3> | ||
| 1437 | \\ Both option. | ||
| 1438 | \\ | ||
| 1439 | \\-d, --dd <V3>... | ||
| 1440 | \\ Both repeated option. | ||
| 1441 | \\ | ||
| 1442 | ); | ||
| 1443 | |||
| 1444 | try testHelp(.{ .indent = 0, .max_width = 26 }, | ||
| 1445 | \\-a | ||
| 1446 | \\ Short flag. | ||
| 1447 | \\ | ||
| 1448 | \\-b <V1> | ||
| 1449 | \\ Short option. | ||
| 1450 | \\ | ||
| 1451 | \\ --aa | ||
| 1452 | \\ Long flag. | ||
| 1453 | \\ | ||
| 1454 | \\ --bb <V2> | ||
| 1455 | \\ Long option. | ||
| 1456 | \\ | ||
| 1457 | \\-c, --cc | ||
| 1458 | \\ Both flag. | ||
| 1459 | \\ | ||
| 1460 | \\ --complicate | ||
| 1461 | \\ Flag with a | ||
| 1462 | \\ complicated and | ||
| 1463 | \\ very long | ||
| 1464 | \\ description that | ||
| 1465 | \\ spans multiple | ||
| 1466 | \\ lines. | ||
| 1467 | \\ | ||
| 1468 | \\ Paragraph number | ||
| 1469 | \\ 2: | ||
| 1470 | \\ * Bullet point | ||
| 1471 | \\ * Bullet point | ||
| 1472 | \\ | ||
| 1473 | \\ Example: | ||
| 1474 | \\ something | ||
| 1475 | \\ something | ||
| 1476 | \\ something | ||
| 1477 | \\ | ||
| 1478 | \\-d, --dd <V3> | ||
| 1479 | \\ Both option. | ||
| 1480 | \\ | ||
| 1481 | \\-d, --dd <V3>... | ||
| 1482 | \\ Both repeated | ||
| 1483 | \\ option. | ||
| 1484 | \\ | ||
| 1485 | ); | ||
| 1486 | |||
| 1487 | try testHelp(.{ | ||
| 1488 | .indent = 0, | ||
| 1489 | .max_width = 26, | ||
| 1490 | .description_indent = 6, | ||
| 1491 | }, | ||
| 1492 | \\-a | ||
| 1493 | \\ Short flag. | ||
| 1494 | \\ | ||
| 1495 | \\-b <V1> | ||
| 1496 | \\ Short option. | ||
| 1497 | \\ | ||
| 1498 | \\ --aa | ||
| 1499 | \\ Long flag. | ||
| 1500 | \\ | ||
| 1501 | \\ --bb <V2> | ||
| 1502 | \\ Long option. | ||
| 1503 | \\ | ||
| 1504 | \\-c, --cc | ||
| 1505 | \\ Both flag. | ||
| 1506 | \\ | ||
| 1507 | \\ --complicate | ||
| 1508 | \\ Flag with a | ||
| 1509 | \\ complicated and | ||
| 1510 | \\ very long | ||
| 1511 | \\ description that | ||
| 1512 | \\ spans multiple | ||
| 1513 | \\ lines. | ||
| 1514 | \\ | ||
| 1515 | \\ Paragraph number 2: | ||
| 1516 | \\ * Bullet point | ||
| 1517 | \\ * Bullet point | ||
| 1518 | \\ | ||
| 1519 | \\ Example: | ||
| 1520 | \\ something | ||
| 1521 | \\ something | ||
| 1522 | \\ something | ||
| 1523 | \\ | ||
| 1524 | \\-d, --dd <V3> | ||
| 1525 | \\ Both option. | ||
| 1526 | \\ | ||
| 1527 | \\-d, --dd <V3>... | ||
| 1528 | \\ Both repeated | ||
| 1529 | \\ option. | ||
| 1530 | \\ | ||
| 1531 | ); | ||
| 1532 | |||
| 1533 | try testHelp(.{ | ||
| 1534 | .indent = 0, | ||
| 1535 | .max_width = 46, | ||
| 1536 | .description_on_new_line = false, | ||
| 1537 | }, | ||
| 1538 | \\-a Short flag. | ||
| 1539 | \\ | ||
| 1540 | \\-b <V1> Short option. | ||
| 1541 | \\ | ||
| 1542 | \\ --aa Long flag. | ||
| 1543 | \\ | ||
| 1544 | \\ --bb <V2> Long option. | ||
| 1545 | \\ | ||
| 1546 | \\-c, --cc Both flag. | ||
| 1547 | \\ | ||
| 1548 | \\ --complicate Flag with a | ||
| 1549 | \\ complicated and very | ||
| 1550 | \\ long description that | ||
| 1551 | \\ spans multiple lines. | ||
| 1552 | \\ | ||
| 1553 | \\ Paragraph number 2: | ||
| 1554 | \\ * Bullet point | ||
| 1555 | \\ * Bullet point | ||
| 1556 | \\ | ||
| 1557 | \\ Example: | ||
| 1558 | \\ something | ||
| 1559 | \\ something | ||
| 1560 | \\ something | ||
| 1561 | \\ | ||
| 1562 | \\-d, --dd <V3> Both option. | ||
| 1563 | \\ | ||
| 1564 | \\-d, --dd <V3>... Both repeated option. | ||
| 1565 | \\ | ||
| 1566 | ); | ||
| 1567 | |||
| 1568 | try testHelp(.{ | ||
| 1569 | .indent = 0, | ||
| 1570 | .max_width = 46, | ||
| 1571 | .description_on_new_line = false, | ||
| 1572 | .description_indent = 4, | ||
| 1573 | }, | ||
| 1574 | \\-a Short flag. | ||
| 1575 | \\ | ||
| 1576 | \\-b <V1> Short option. | ||
| 1577 | \\ | ||
| 1578 | \\ --aa Long flag. | ||
| 1579 | \\ | ||
| 1580 | \\ --bb <V2> Long option. | ||
| 1581 | \\ | ||
| 1582 | \\-c, --cc Both flag. | ||
| 1583 | \\ | ||
| 1584 | \\ --complicate Flag with a complicated | ||
| 1585 | \\ and very long description | ||
| 1586 | \\ that spans multiple | ||
| 1587 | \\ lines. | ||
| 1588 | \\ | ||
| 1589 | \\ Paragraph number 2: | ||
| 1590 | \\ * Bullet point | ||
| 1591 | \\ * Bullet point | ||
| 1592 | \\ | ||
| 1593 | \\ Example: | ||
| 1594 | \\ something something | ||
| 1595 | \\ something | ||
| 1596 | \\ | ||
| 1597 | \\-d, --dd <V3> Both option. | ||
| 1598 | \\ | ||
| 1599 | \\-d, --dd <V3>... Both repeated option. | ||
| 1600 | \\ | ||
| 1601 | ); | ||
| 1602 | |||
| 1603 | try testHelp(.{ | ||
| 1604 | .indent = 0, | ||
| 1605 | .max_width = 46, | ||
| 1606 | .description_on_new_line = false, | ||
| 1607 | .description_indent = 4, | ||
| 1608 | .spacing_between_parameters = 0, | ||
| 1609 | }, | ||
| 1610 | \\-a Short flag. | ||
| 1611 | \\-b <V1> Short option. | ||
| 1612 | \\ --aa Long flag. | ||
| 1613 | \\ --bb <V2> Long option. | ||
| 1614 | \\-c, --cc Both flag. | ||
| 1615 | \\ --complicate Flag with a complicated | ||
| 1616 | \\ and very long description | ||
| 1617 | \\ that spans multiple | ||
| 1618 | \\ lines. | ||
| 1619 | \\ | ||
| 1620 | \\ Paragraph number 2: | ||
| 1621 | \\ * Bullet point | ||
| 1622 | \\ * Bullet point | ||
| 1623 | \\ | ||
| 1624 | \\ Example: | ||
| 1625 | \\ something something | ||
| 1626 | \\ something | ||
| 1627 | \\-d, --dd <V3> Both option. | ||
| 1628 | \\-d, --dd <V3>... Both repeated option. | ||
| 1629 | \\ | ||
| 1630 | ); | ||
| 1631 | |||
| 1632 | try testHelp(.{ | ||
| 1633 | .indent = 0, | ||
| 1634 | .max_width = 46, | ||
| 1635 | .description_on_new_line = false, | ||
| 1636 | .description_indent = 4, | ||
| 1637 | .spacing_between_parameters = 2, | ||
| 1638 | }, | ||
| 1639 | \\-a Short flag. | ||
| 1640 | \\ | ||
| 1641 | \\ | ||
| 1642 | \\-b <V1> Short option. | ||
| 1643 | \\ | ||
| 1644 | \\ | ||
| 1645 | \\ --aa Long flag. | ||
| 1646 | \\ | ||
| 1647 | \\ | ||
| 1648 | \\ --bb <V2> Long option. | ||
| 1649 | \\ | ||
| 1650 | \\ | ||
| 1651 | \\-c, --cc Both flag. | ||
| 1652 | \\ | ||
| 1653 | \\ | ||
| 1654 | \\ --complicate Flag with a complicated | ||
| 1655 | \\ and very long description | ||
| 1656 | \\ that spans multiple | ||
| 1657 | \\ lines. | ||
| 1658 | \\ | ||
| 1659 | \\ Paragraph number 2: | ||
| 1660 | \\ * Bullet point | ||
| 1661 | \\ * Bullet point | ||
| 1662 | \\ | ||
| 1663 | \\ Example: | ||
| 1664 | \\ something something | ||
| 1665 | \\ something | ||
| 1666 | \\ | ||
| 1667 | \\ | ||
| 1668 | \\-d, --dd <V3> Both option. | ||
| 1669 | \\ | ||
| 1670 | \\ | ||
| 1671 | \\-d, --dd <V3>... Both repeated option. | ||
| 1672 | \\ | ||
| 1673 | ); | ||
| 1079 | } | 1674 | } |
| 1080 | 1675 | ||
| 1081 | /// Will print a usage message in the following format: | 1676 | /// Will print a usage message in the following format: |
| @@ -1104,7 +1699,6 @@ pub fn usage(stream: anytype, comptime Id: type, params: []const Param(Id)) !voi | |||
| 1104 | continue; | 1699 | continue; |
| 1105 | 1700 | ||
| 1106 | const prefix = if (param.names.short) |_| "-" else "--"; | 1701 | const prefix = if (param.names.short) |_| "-" else "--"; |
| 1107 | |||
| 1108 | const name = if (param.names.short) |*s| | 1702 | const name = if (param.names.short) |*s| |
| 1109 | // Seems the zig compiler is being a little wierd. I doesn't allow me to write | 1703 | // Seems the zig compiler is being a little wierd. I doesn't allow me to write |
| 1110 | // @as(*const [1]u8, s) | 1704 | // @as(*const [1]u8, s) |
diff --git a/example/README.md.template b/example/README.md.template index b76ae45..8b12cd6 100644 --- a/example/README.md.template +++ b/example/README.md.template | |||
| @@ -58,7 +58,8 @@ is generated at runtime. | |||
| 58 | 58 | ||
| 59 | The `help` prints a simple list of all parameters the program can take. It expects the | 59 | The `help` prints a simple list of all parameters the program can take. It expects the |
| 60 | `Id` to have a `description` method and an `value` method so that it can provide that | 60 | `Id` to have a `description` method and an `value` method so that it can provide that |
| 61 | in the output. | 61 | in the output. `HelpOptions` is passed to `help` to control how the help message is |
| 62 | printed. | ||
| 62 | 63 | ||
| 63 | ```zig | 64 | ```zig |
| 64 | {s} | 65 | {s} |
| @@ -66,8 +67,11 @@ in the output. | |||
| 66 | 67 | ||
| 67 | ``` | 68 | ``` |
| 68 | $ zig-out/bin/help --help | 69 | $ zig-out/bin/help --help |
| 69 | -h, --help Display this help and exit. | 70 | -h, --help |
| 70 | -v, --version Output version information and exit. | 71 | Display this help and exit. |
| 72 | |||
| 73 | -v, --version | ||
| 74 | Output version information and exit. | ||
| 71 | ``` | 75 | ``` |
| 72 | 76 | ||
| 73 | ### `usage` | 77 | ### `usage` |
diff --git a/example/help.zig b/example/help.zig index 64d1709..18d61b9 100644 --- a/example/help.zig +++ b/example/help.zig | |||
| @@ -15,5 +15,5 @@ pub fn main() !void { | |||
| 15 | // slice of Param(Help). There is also a helpEx, which can print a | 15 | // slice of Param(Help). There is also a helpEx, which can print a |
| 16 | // help message for any Param, but it is more verbose to call. | 16 | // help message for any Param, but it is more verbose to call. |
| 17 | if (res.args.help) | 17 | if (res.args.help) |
| 18 | return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms); | 18 | return clap.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{}); |
| 19 | } | 19 | } |