@@ -137,7 +137,7 @@ class format_base
137137 *
138138 * \details Special characters considered are `"`, `\`, `&`, `<` and `>`.
139139 */
140- std::string escape_special_xml_chars (std::string const & original)
140+ static std::string escape_special_xml_chars (std::string const & original)
141141 {
142142 std::string escaped;
143143 escaped.reserve (original.size ()); // will be at least as long
@@ -183,6 +183,62 @@ class format_base
183183
184184 return tmp;
185185 }
186+
187+ /* !\brief Returns the default message for the help page.
188+ * \tparam option_type The type of the option.
189+ * \tparam default_type The type of the default value.
190+ * \param[in] option The option to get the default message for.
191+ * \param[in] value The default value to get the default message for.
192+ * \returns The default message for the help page (" Default: <default-value>. ").
193+ * \details
194+ * `value` is either `config.default_message`, or the same as `option`.
195+ * If the `option_type` is a std::string or std::filesystem::path, the value is quoted.
196+ * If the `option_type` is a container of std::string or std::filesystem::path, each individual value is quoted;
197+ * if a `config.default_message` is provided, it will not be quoted.
198+ */
199+ template <typename option_type, typename default_type>
200+ static std::string get_default_message (option_type const & SHARG_DOXYGEN_ONLY (option), default_type const & value)
201+ {
202+ static_assert (std::same_as<option_type, default_type> || std::same_as<default_type, std::string>);
203+
204+ std::stringstream message{};
205+ message << " Default: " ;
206+
207+ if constexpr (detail::is_container_option<option_type>)
208+ {
209+ // If we have a list of strings, we want to quote each string.
210+ if constexpr (std::same_as<std::ranges::range_value_t <default_type>, std::string>)
211+ {
212+ auto view = std::views::transform (value,
213+ [](auto const & val)
214+ {
215+ return std::quoted (val);
216+ });
217+ message << detail::to_string (view);
218+ }
219+ else // Otherwise we just print the list or the default_message without quotes.
220+ {
221+ message << detail::to_string (value);
222+ }
223+ }
224+ else
225+ {
226+ static constexpr bool option_is_string = std::same_as<option_type, std::string>;
227+ static constexpr bool option_is_path = std::same_as<option_type, std::filesystem::path>;
228+ static constexpr bool value_is_string = std::same_as<default_type, std::string>;
229+
230+ // Quote: std::string (from value + default_message), and std::filesystem::path (default_message).
231+ // std::filesystem::path is quoted by the STL's operator<< in detail::to_string.
232+ static constexpr bool needs_string_quote = option_is_string || (option_is_path && value_is_string);
233+
234+ if constexpr (needs_string_quote)
235+ message << std::quoted (value);
236+ else
237+ message << detail::to_string (value);
238+ }
239+
240+ return message.str ();
241+ }
186242};
187243
188244/* !\brief The format that contains all helper functions needed in all formats for
@@ -227,12 +283,14 @@ class format_help_base : public format_base
227283 {
228284 std::string id = prep_id_for_help (config.short_id , config.long_id ) + " " + option_type_and_list_info (value);
229285 std::string info{config.description };
286+
230287 if (config.default_message .empty ())
231- info += ((config.required ) ? std::string{" " } : detail::to_string ( " Default: " , value, " . " ));
288+ info += ((config.required ) ? std::string{} : get_default_message ( value, value ));
232289 else
233- info += detail::to_string ( " Default: " , config.default_message , " . " );
290+ info += get_default_message (value , config.default_message );
234291
235- info += config.validator .get_help_page_message ();
292+ if (auto const & validator_message = config.validator .get_help_page_message (); !validator_message.empty ())
293+ info += " . " + validator_message;
236294
237295 store_help_page_element (
238296 [this , id, info]()
@@ -262,20 +320,41 @@ class format_help_base : public format_base
262320 template <typename option_type, typename validator_t >
263321 void add_positional_option (option_type & value, config<validator_t > const & config)
264322 {
323+ // a list at the end may be empty and thus have a default value
324+ auto positional_default_message = [&value]() -> std::string
325+ {
326+ if constexpr (detail::is_container_option<option_type>)
327+ {
328+ return get_default_message (value, value);
329+ }
330+ else
331+ {
332+ (void )value; // Silence unused variable warning.
333+ return {};
334+ }
335+ };
336+
337+ auto positional_validator_message = [&config]() -> std::string
338+ {
339+ if (auto const & validator_message = config.validator .get_help_page_message (); !validator_message.empty ())
340+ return " . " + validator_message;
341+ else
342+ return {};
343+ };
344+
265345 positional_option_calls.push_back (
266- [this , &value, description = config.description , validator = config.validator ]()
346+ [this ,
347+ &value,
348+ default_message = positional_default_message (),
349+ validator_message = positional_validator_message (),
350+ description = config.description ]()
267351 {
268352 ++positional_option_count;
269353 derived_t ().print_list_item (detail::to_string (" \\ fBARGUMENT-" ,
270354 positional_option_count,
271355 " \\ fP " ,
272356 option_type_and_list_info (value)),
273- description +
274- // a list at the end may be empty and thus have a default value
275- ((detail::is_container_option<option_type>)
276- ? detail::to_string (" Default: " , value, " . " )
277- : std::string{" " })
278- + validator.get_help_page_message ());
357+ description + default_message + validator_message);
279358 });
280359 }
281360
@@ -343,7 +422,7 @@ class format_help_base : public format_base
343422 + detail::supported_exports + " ." );
344423 if (version_check_dev_decision == update_notifications::on)
345424 derived_t ().print_list_item (" \\ fB--version-check\\ fP (bool)" ,
346- " Whether to check for the newest app version. Default: true. " );
425+ " Whether to check for the newest app version. Default: true" );
347426
348427 if (!meta.examples .empty ())
349428 {
0 commit comments