on 8d0aa39 help sign in
bat/src/clap_app.rs
432 lines 19 kB history raw
1use clap::{App as ClapApp, AppSettings, Arg, ArgGroup, SubCommand};
2use std::path::Path;
4pub fn build_app(interactive_output: bool) -> ClapApp<'static, 'static> {
5 let clap_color_setting = if interactive_output {
6 AppSettings::ColoredHelp
7 } else {
8 AppSettings::ColorNever
9 };
11 // Check if the current directory contains a file name cache, if it does
12 // do not make the arguements for subcommand 'cache' required.
13 let arg_group_required = !Path::new("cache").exists();
15 ClapApp::new(crate_name!())
16 .version(crate_version!())
17 .global_setting(clap_color_setting)
18 .global_setting(AppSettings::DeriveDisplayOrder)
19 .global_setting(AppSettings::UnifiedHelpMessage)
20 .global_setting(AppSettings::HidePossibleValuesInHelp)
21 .setting(AppSettings::ArgsNegateSubcommands)
22 .setting(AppSettings::AllowExternalSubcommands)
23 .setting(AppSettings::DisableHelpSubcommand)
24 .setting(AppSettings::VersionlessSubcommands)
25 .max_term_width(100)
26 .about(
27 "A cat(1) clone with wings.\n\n\
28 Use '--help' instead of '-h' to see a more detailed version of the help text.",
29 )
30 .long_about("A cat(1) clone with syntax highlighting and Git integration.")
31 .arg(
32 Arg::with_name("FILE")
33 .help("File(s) to print / concatenate. Use '-' for standard input.")
34 .long_help(
35 "File(s) to print / concatenate. Use a dash ('-') or no argument at all \
36 to read from standard input.",
37 )
38 .multiple(true)
39 .empty_values(false),
40 )
41 .arg(
42 Arg::with_name("language")
43 .short("l")
44 .long("language")
45 .overrides_with("language")
46 .help("Set the language for syntax highlighting.")
47 .long_help(
48 "Explicitly set the language for syntax highlighting. The language can be \
49 specified as a name (like 'C++' or 'LaTeX') or possible file extension \
50 (like 'cpp', 'hpp' or 'md'). Use '--list-languages' to show all supported \
51 language names and file extensions.",
52 )
53 .takes_value(true),
54 )
55 .arg(
56 Arg::with_name("list-languages")
57 .long("list-languages")
58 .short("L")
59 .conflicts_with("list-themes")
60 .help("Display all supported languages.")
61 .long_help("Display a list of supported languages for syntax highlighting."),
62 )
63 .arg(
64 Arg::with_name("map-syntax")
65 .short("m")
66 .long("map-syntax")
67 .multiple(true)
68 .takes_value(true)
69 .number_of_values(1)
70 .value_name("from:to")
71 .help("Map a file extension or name to an existing syntax.")
72 .long_help(
73 "Map a file extension or file name to an existing syntax. For example, \
74 to highlight *.conf files with the INI syntax, use '-m conf:ini'. \
75 To highlight files named '.myignore' with the Git Ignore syntax, use \
76 '-m .myignore:gitignore'.",
77 )
78 .takes_value(true),
79 )
80 .arg(
81 Arg::with_name("theme")
82 .long("theme")
83 .overrides_with("theme")
84 .takes_value(true)
85 .help("Set the color theme for syntax highlighting.")
86 .long_help(
87 "Set the theme for syntax highlighting. Use '--list-themes' to \
88 see all available themes. To set a default theme, add the \
89 '--theme=\"...\"' option to the configuration file or export the \
90 BAT_THEME environment variable (e.g.: export \
91 BAT_THEME=\"...\").",
92 ),
93 )
94 .arg(
95 Arg::with_name("list-themes")
96 .long("list-themes")
97 .help("Display all supported highlighting themes.")
98 .long_help("Display a list of supported themes for syntax highlighting."),
99 )
100 .arg(
101 Arg::with_name("style")
102 .long("style")
103 .value_name("style-components")
104 // Need to turn this off for overrides_with to work as we want. See the bottom most
105 // example at https://docs.rs/clap/2.32.0/clap/struct.Arg.html#method.overrides_with
106 .use_delimiter(false)
107 .takes_value(true)
108 .overrides_with("style")
109 .overrides_with("plain")
110 .overrides_with("number")
111 // Cannot use clap's built in validation because we have to turn off clap's delimiters
112 .validator(|val| {
113 let mut invalid_vals = val.split(',').filter(|style| {
114 !&[
115 "auto", "full", "plain", "changes", "header", "grid", "numbers", "snip"
116 ]
117 .contains(style)
118 });
120 if let Some(invalid) = invalid_vals.next() {
121 Err(format!("Unknown style, '{}'", invalid))
122 } else {
123 Ok(())
124 }
125 })
126 .help(
127 "Comma-separated list of style elements to display \
128 (*auto*, full, plain, changes, header, grid, numbers, snip).",
129 )
130 .long_help(
131 "Configure which elements (line numbers, file headers, grid \
132 borders, Git modifications, ..) to display in addition to the \
133 file contents. The argument is a comma-separated list of \
134 components to display (e.g. 'numbers,changes,grid') or a \
135 pre-defined style ('full'). To set a default style, add the \
136 '--style=\"..\"' option to the configuration file or export the \
137 BAT_STYLE environment variable (e.g.: export BAT_STYLE=\"..\"). \
138 Possible values: *auto*, full, plain, changes, header, grid, numbers, snip.",
139 ),
140 )
141 .arg(
142 Arg::with_name("plain")
143 .overrides_with("plain")
144 .overrides_with("number")
145 .short("p")
146 .long("plain")
147 .multiple(true)
148 .help("Show plain style (alias for '--style=plain').")
149 .long_help(
150 "Only show plain style, no decorations. This is an alias for \
151 '--style=plain'. When '-p' is used twice ('-pp'), it also disables \
152 automatic paging (alias for '--style=plain --pager=never').",
153 ),
154 )
155 .arg(
156 Arg::with_name("number")
157 .long("number")
158 .overrides_with("number")
159 .short("n")
160 .help("Show line numbers (alias for '--style=numbers').")
161 .long_help(
162 "Only show line numbers, no other decorations. This is an alias for \
163 '--style=numbers'",
164 ),
165 )
166 .arg(
167 Arg::with_name("show-all")
168 .long("show-all")
169 .alias("show-nonprintable")
170 .short("A")
171 .conflicts_with("language")
172 .help("Show non-printable characters (space, tab, newline, ..).")
173 .long_help(
174 "Show non-printable characters like space, tab or newline. \
175 This option can also be used to print binary files. \
176 Use '--tabs' to control the width of the tab-placeholders."
177 ),
178 )
179 .arg(
180 Arg::with_name("line-range")
181 .long("line-range")
182 .short("r")
183 .multiple(true)
184 .takes_value(true)
185 .number_of_values(1)
186 .value_name("N:M")
187 .help("Only print the lines from N to M.")
188 .long_help(
189 "Only print the specified range of lines for each file. \
190 For example:\n \
191 '--line-range 30:40' prints lines 30 to 40\n \
192 '--line-range :40' prints lines 1 to 40\n \
193 '--line-range 40:' prints lines 40 to the end of the file",
194 ),
195 )
196 .arg(
197 Arg::with_name("highlight-line")
198 .long("highlight-line")
199 .short("H")
200 .takes_value(true)
201 .number_of_values(1)
202 .multiple(true)
203 .value_name("N")
204 .help("Highlight the given line.")
205 .long_help(
206 "Highlight the N-th line with a different background color",
207 ),
208 )
209 .arg(
210 Arg::with_name("color")
211 .long("color")
212 .overrides_with("color")
213 .takes_value(true)
214 .value_name("when")
215 .possible_values(&["auto", "never", "always"])
216 .hide_default_value(true)
217 .default_value("auto")
218 .help("When to use colors (*auto*, never, always).")
219 .long_help(
220 "Specify when to use colored output. The automatic mode \
221 only enables colors if an interactive terminal is detected. \
222 Possible values: *auto*, never, always.",
223 ),
224 )
225 .arg(
226 Arg::with_name("italic-text")
227 .long("italic-text")
228 .takes_value(true)
229 .value_name("when")
230 .possible_values(&["always", "never"])
231 .default_value("never")
232 .hide_default_value(true)
233 .help("Use italics in output (always, *never*)")
234 .long_help("Specify when to use ANSI sequences for italic text in the output. Possible values: always, *never*."),
235 )
236 .arg(
237 Arg::with_name("decorations")
238 .long("decorations")
239 .overrides_with("decorations")
240 .takes_value(true)
241 .value_name("when")
242 .possible_values(&["auto", "never", "always"])
243 .default_value("auto")
244 .hide_default_value(true)
245 .help("When to show the decorations (*auto*, never, always).")
246 .long_help(
247 "Specify when to use the decorations that have been specified \
248 via '--style'. The automatic mode only enables decorations if \
249 an interactive terminal is detected. Possible values: *auto*, never, always.",
250 ),
251 )
252 .arg(
253 Arg::with_name("paging")
254 .long("paging")
255 .overrides_with("paging")
256 .takes_value(true)
257 .value_name("when")
258 .possible_values(&["auto", "never", "always"])
259 .default_value("auto")
260 .hide_default_value(true)
261 .help("Specify when to use the pager (*auto*, never, always).")
262 .long_help(
263 "Specify when to use the pager. To control which pager \
264 is used, set the PAGER or BAT_PAGER environment \
265 variables (the latter takes precedence) or use the '--pager' option. \
266 To disable the pager permanently, set BAT_PAGER to an empty string \
267 or set '--paging=never' in the configuration file. \
268 Possible values: *auto*, never, always.",
269 ),
270 )
271 .arg(
272 Arg::with_name("pager")
273 .long("pager")
274 .overrides_with("pager")
275 .takes_value(true)
276 .value_name("command")
277 .hidden_short_help(true)
278 .help("Determine which pager to use.")
279 .long_help(
280 "Determine which pager is used. This option will overwrite \
281 the PAGER and BAT_PAGER environment variables. The default \
282 pager is 'less'. To disable the pager completely, use the \
283 '--paging' option. \
284 Example: '--pager \"less -RF\"'.",
285 ),
286 )
287 .arg(
288 Arg::with_name("wrap")
289 .long("wrap")
290 .overrides_with("wrap")
291 .takes_value(true)
292 .value_name("mode")
293 .possible_values(&["auto", "never", "character"])
294 .default_value("auto")
295 .hide_default_value(true)
296 .help("Specify the text-wrapping mode (*auto*, never, character).")
297 .long_help("Specify the text-wrapping mode (*auto*, never, character). \
298 The '--terminal-width' option can be used in addition to \
299 control the output width."),
300 )
301 .arg(
302 Arg::with_name("tabs")
303 .long("tabs")
304 .overrides_with("tabs")
305 .takes_value(true)
306 .value_name("T")
307 .validator(
308 |t| {
309 t.parse::<u32>()
310 .map_err(|_t| "must be a number")
311 .map(|_t| ()) // Convert to Result<(), &str>
312 .map_err(|e| e.to_string())
313 }, // Convert to Result<(), String>
314 )
315 .help("Set the tab width to T spaces.")
316 .long_help(
317 "Set the tab width to T spaces. Use a width of 0 to pass tabs through \
318 directly",
319 ),
320 )
321 .arg(
322 Arg::with_name("unbuffered")
323 .short("u")
324 .long("unbuffered")
325 .hidden_short_help(true)
326 .long_help(
327 "This option exists for POSIX-compliance reasons ('u' is for \
328 'unbuffered'). The output is always unbuffered - this option \
329 is simply ignored.",
330 ),
331 )
332 .arg(
333 Arg::with_name("terminal-width")
334 .long("terminal-width")
335 .takes_value(true)
336 .value_name("width")
337 .hidden_short_help(true)
338 .allow_hyphen_values(true)
339 .validator(
340 |t| {
341 let is_offset = t.starts_with('+') || t.starts_with('-');
342 t.parse::<i32>()
343 .map_err(|_e| "must be an offset or number")
344 .and_then(|v| if v == 0 && !is_offset {
345 Err("terminal width cannot be zero".into())
346 } else {
347 Ok(())
348 })
349 .map_err(|e| e.to_string())
350 })
351 .help(
352 "Explicitly set the width of the terminal instead of determining it \
353 automatically. If prefixed with '+' or '-', the value will be treated \
354 as an offset to the actual terminal width. See also: '--wrap'.",
355 ),
356 )
357 .arg(
358 Arg::with_name("no-config")
359 .long("no-config")
360 .hidden(true)
361 .help("Do not use the configuration file"),
362 )
363 .arg(
364 Arg::with_name("config-file")
365 .long("config-file")
366 .conflicts_with("list-languages")
367 .conflicts_with("list-themes")
368 .hidden(true)
369 .help("Show path to the configuration file."),
370 )
371 .arg(
372 Arg::with_name("config-dir")
373 .long("config-dir")
374 .hidden(true)
375 .help("Show bat's configuration directory."),
376 )
377 .arg(
378 Arg::with_name("cache-dir")
379 .long("cache-dir")
380 .hidden(true)
381 .help("Show bat's cache directory."),
382 )
383 .subcommand(
384 SubCommand::with_name("cache")
385 .about("Modify the syntax-definition and theme cache")
386 .arg(
387 Arg::with_name("build")
388 .long("build")
389 .short("b")
390 .help("Initialize (or update) the syntax/theme cache.")
391 .long_help(
392 "Initialize (or update) the syntax/theme cache by loading from \
393 the source directory (default: the configuration directory).",
394 ),
395 )
396 .arg(
397 Arg::with_name("clear")
398 .long("clear")
399 .short("c")
400 .help("Remove the cached syntax definitions and themes."),
401 )
402 .group(
403 ArgGroup::with_name("cache-actions")
404 .args(&["build", "clear"])
405 .required(arg_group_required),
406 )
407 .arg(
408 Arg::with_name("source")
409 .long("source")
410 .requires("build")
411 .takes_value(true)
412 .value_name("dir")
413 .help("Use a different directory to load syntaxes and themes from."),
414 )
415 .arg(
416 Arg::with_name("target")
417 .long("target")
418 .requires("build")
419 .takes_value(true)
420 .value_name("dir")
421 .help(
422 "Use a different directory to store the cached syntax and theme set.",
423 ),
424 )
425 .arg(Arg::with_name("blank").long("blank").requires("build").help(
426 "Create completely new syntax and theme sets \
427 (instead of appending to the default sets).",
428 )),
429 )
430 .help_message("Print this help message.")
431 .version_message("Show version information.")