@@ -38,6 +38,11 @@ pub fn env_no_color() -> bool {
3838 env:: var_os ( "NO_COLOR" ) . is_some_and ( |x| !x. is_empty ( ) )
3939}
4040
41+ enum HelpType {
42+ Short ,
43+ Long ,
44+ }
45+
4146pub struct App {
4247 pub matches : ArgMatches ,
4348 interactive_output : bool ,
@@ -49,53 +54,155 @@ impl App {
4954 let _ = nu_ansi_term:: enable_ansi_support ( ) ;
5055
5156 let interactive_output = std:: io:: stdout ( ) . is_terminal ( ) ;
57+ let matches = Self :: matches ( interactive_output) ?;
58+
59+ if matches. get_flag ( "help" ) {
60+ let help_type = if wild:: args_os ( ) . any ( |arg| arg == "--help" ) {
61+ HelpType :: Long
62+ } else {
63+ HelpType :: Short
64+ } ;
65+
66+ let use_pager = match matches. get_one :: < String > ( "paging" ) . map ( |s| s. as_str ( ) ) {
67+ Some ( "never" ) => false ,
68+ _ => !matches. get_flag ( "no-paging" ) ,
69+ } ;
70+
71+ let use_color = match matches. get_one :: < String > ( "color" ) . map ( |s| s. as_str ( ) ) {
72+ Some ( "always" ) => true ,
73+ Some ( "never" ) => false ,
74+ _ => interactive_output, // auto: use color if interactive
75+ } ;
76+
77+ let custom_pager = matches. get_one :: < String > ( "pager" ) . map ( |s| s. to_string ( ) ) ;
78+ let theme_options = Self :: theme_options_from_matches ( & matches) ;
79+
80+ Self :: display_help (
81+ interactive_output,
82+ help_type,
83+ use_pager,
84+ use_color,
85+ custom_pager,
86+ theme_options,
87+ ) ?;
88+ std:: process:: exit ( 0 ) ;
89+ }
5290
5391 Ok ( App {
54- matches : Self :: matches ( interactive_output ) ? ,
92+ matches,
5593 interactive_output,
5694 } )
5795 }
5896
97+ fn display_help (
98+ interactive_output : bool ,
99+ help_type : HelpType ,
100+ use_pager : bool ,
101+ use_color : bool ,
102+ custom_pager : Option < String > ,
103+ theme_options : ThemeOptions ,
104+ ) -> Result < ( ) > {
105+ use crate :: assets:: assets_from_cache_or_binary;
106+ use crate :: directories:: PROJECT_DIRS ;
107+ use bat:: {
108+ config:: Config ,
109+ controller:: Controller ,
110+ input:: Input ,
111+ style:: { StyleComponent , StyleComponents } ,
112+ theme:: theme,
113+ PagingMode ,
114+ } ;
115+
116+ let mut cmd = clap_app:: build_app ( interactive_output) ;
117+ let help_text = match help_type {
118+ HelpType :: Short => cmd. render_help ( ) . to_string ( ) ,
119+ HelpType :: Long => cmd. render_long_help ( ) . to_string ( ) ,
120+ } ;
121+
122+ let inputs: Vec < Input > = vec ! [ Input :: from_reader( Box :: new( help_text. as_bytes( ) ) ) ] ;
123+
124+ let paging_mode = if use_pager {
125+ PagingMode :: QuitIfOneScreen
126+ } else {
127+ PagingMode :: Never
128+ } ;
129+
130+ let pager = bat:: config:: get_pager_executable ( custom_pager. as_deref ( ) ) ;
131+
132+ let help_config = Config {
133+ style_components : StyleComponents :: new ( StyleComponent :: Plain . components ( false ) ) ,
134+ paging_mode,
135+ pager : pager. as_deref ( ) ,
136+ colored_output : use_color,
137+ true_color : use_color,
138+ language : if use_color { Some ( "help" ) } else { None } ,
139+ theme : theme ( theme_options) . to_string ( ) ,
140+ ..Default :: default ( )
141+ } ;
142+
143+ let cache_dir = PROJECT_DIRS . cache_dir ( ) ;
144+ let assets = assets_from_cache_or_binary ( false , cache_dir) ?;
145+ Controller :: new ( & help_config, & assets)
146+ . run ( inputs, None )
147+ . ok ( ) ;
148+
149+ Ok ( ( ) )
150+ }
151+
59152 fn matches ( interactive_output : bool ) -> Result < ArgMatches > {
60153 // Check if we should skip config file processing for special arguments
61- // that don't require full application setup (help, version, diagnostic)
154+ // that don't require full application setup (version, diagnostic)
62155 let should_skip_config = wild:: args_os ( ) . any ( |arg| {
63156 matches ! (
64157 arg. to_str( ) ,
65- Some ( "-h" | "--help" | "- V" | "--version" | "--diagnostic" | "--diagnostics" )
158+ Some ( "-V" | "--version" | "--diagnostic" | "--diagnostics" )
66159 )
67160 } ) ;
68161
162+ // Check if help was requested - help should go through the same code path
163+ // but be forgiving of config file errors
164+ let help_requested =
165+ wild:: args_os ( ) . any ( |arg| matches ! ( arg. to_str( ) , Some ( "-h" | "--help" ) ) ) ;
166+
69167 let args = if wild:: args_os ( ) . nth ( 1 ) == Some ( "cache" . into ( ) ) {
70168 // Skip the config file and env vars
71169
72170 wild:: args_os ( ) . collect :: < Vec < _ > > ( )
73171 } else if wild:: args_os ( ) . any ( |arg| arg == "--no-config" ) || should_skip_config {
74172 // Skip the arguments in bats config file when --no-config is present
75- // or when user requests help, version, or diagnostic information
173+ // or when user requests version or diagnostic information
76174
77175 let mut cli_args = wild:: args_os ( ) ;
78- let mut args = if should_skip_config {
79- // For special commands, don't even try to load env vars that might fail
80- vec ! [ ]
81- } else {
82- get_args_from_env_vars ( )
83- } ;
176+ let mut args = get_args_from_env_vars ( ) ;
84177
85178 // Put the zero-th CLI argument (program name) first
86179 args. insert ( 0 , cli_args. next ( ) . unwrap ( ) ) ;
87180
88181 // .. and the rest at the end
89182 cli_args. for_each ( |a| args. push ( a) ) ;
90183
184+ args
185+ } else if help_requested {
186+ // Help goes through the normal config path but only uses env vars for themes
187+ // to avoid failing on invalid config options
188+ let mut cli_args = wild:: args_os ( ) ;
189+ let mut args = get_args_from_env_vars ( ) ;
190+
191+ // Put the zero-th CLI argument (program name) first
192+ args. insert ( 0 , cli_args. next ( ) . unwrap ( ) ) ;
193+
194+ // .. and the rest at the end (includes --help and other CLI args)
195+ cli_args. for_each ( |a| args. push ( a) ) ;
91196 args
92197 } else {
93198 let mut cli_args = wild:: args_os ( ) ;
94199
95200 // Read arguments from bats config file
96- let mut args = get_args_from_env_opts_var ( )
97- . unwrap_or_else ( get_args_from_config_file)
98- . map_err ( |_| "Could not parse configuration file" ) ?;
201+ let mut args = match get_args_from_env_opts_var ( ) {
202+ Some ( result) => result,
203+ None => get_args_from_config_file ( ) ,
204+ }
205+ . map_err ( |_| "Could not parse configuration file" ) ?;
99206
100207 // Selected env vars supersede config vars
101208 args. extend ( get_args_from_env_vars ( ) ) ;
@@ -462,17 +569,18 @@ impl App {
462569 }
463570
464571 fn theme_options ( & self ) -> ThemeOptions {
465- let theme = self
466- . matches
572+ Self :: theme_options_from_matches ( & self . matches )
573+ }
574+
575+ fn theme_options_from_matches ( matches : & ArgMatches ) -> ThemeOptions {
576+ let theme = matches
467577 . get_one :: < String > ( "theme" )
468578 . map ( |t| ThemePreference :: from_str ( t) . unwrap ( ) )
469579 . unwrap_or_default ( ) ;
470- let theme_dark = self
471- . matches
580+ let theme_dark = matches
472581 . get_one :: < String > ( "theme-dark" )
473582 . map ( |t| ThemeName :: from_str ( t) . unwrap ( ) ) ;
474- let theme_light = self
475- . matches
583+ let theme_light = matches
476584 . get_one :: < String > ( "theme-light" )
477585 . map ( |t| ThemeName :: from_str ( t) . unwrap ( ) ) ;
478586 ThemeOptions {
0 commit comments