diff --git a/share/functions/__fish_print_help.fish b/share/functions/__fish_print_help.fish index 0d74aa084..462068bb3 100644 --- a/share/functions/__fish_print_help.fish +++ b/share/functions/__fish_print_help.fish @@ -1,4 +1,4 @@ -function __fish_print_help --description "Print help message for the specified fish function or builtin" --argument item +function __fish_print_help --description "Print help message for the specified fish function or builtin" --argument item error_message if test "$item" = '.' set item source end @@ -53,6 +53,8 @@ function __fish_print_help --description "Print help message for the specified f # the `-s` flag to `less` that `man` passes. set -l state blank set -l have_name + begin + string join \n $error_message for line in $help # categorize the line set -l line_type @@ -103,5 +105,22 @@ function __fish_print_help --description "Print help message for the specified f # skip it end end - end | string replace -ra '^ ' '' | ul # post-process with `ul`, to interpret the old-style grotty escapes + end + end | string replace -ra '^ ' '' | ul | # post-process with `ul`, to interpret the old-style grotty escapes + begin + set -l pager less + set -q PAGER + and set pager $PAGER + not isatty stdout + and set pager cat # cannot use a builtin here + # similar to man, but add -F to quit paging when the help output is brief (#6227) + set -xl LESS "$LESS"isrFX + # less options: + # -i (--ignore-case) search case-insensitively, like man + # -s (--squeeze-blank-lines) not strictly necessary since we already do that above + # -r (--raw-control-chars) to display bold, underline and colors + # -F (--quit-if-one-screen) to maintain the non-paging behavior for small outputs + # -X (--no-init) not sure if this is needed but git uses it + $pager + end end diff --git a/src/builtin.cpp b/src/builtin.cpp index 0d7ecc7f2..5584231e7 100644 --- a/src/builtin.cpp +++ b/src/builtin.cpp @@ -146,42 +146,27 @@ int parse_help_only_cmd_opts(struct help_only_cmd_opts_t &opts, int *optind, int return STATUS_CMD_OK; } -/// Obtain help/usage information for the specified builtin from manpage in subshell +/// Display help/usage information for the specified builtin or function from manpage /// /// @param name -/// builtin name to get up help for +/// builtin or function name to get up help for /// -/// @return -/// A wcstring with a formatted manpage. -/// -wcstring builtin_help_get(parser_t &parser, io_streams_t &streams, const wchar_t *name) { - UNUSED(parser); +/// Process and print help for the specified builtin or function. +void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *name, + wcstring *error_message) { UNUSED(streams); // This won't ever work if no_exec is set. - if (no_exec()) return wcstring(); - - wcstring_list_t lst; - wcstring out; - const wcstring name_esc = escape_string(name, 1); - wcstring cmd = format_string(L"__fish_print_help %ls", name_esc.c_str()); - if (exec_subshell(cmd, parser, lst, false /* don't apply exit status */) >= 0) { - for (size_t i = 0; i < lst.size(); i++) { - out.append(lst.at(i)); - out.push_back(L'\n'); - } + if (no_exec()) return; + const wcstring name_esc = escape_string(name, ESCAPE_ALL); + wcstring cmd = format_string(L"__fish_print_help %ls ", name_esc.c_str()); + io_chain_t ios; + if (error_message) { + cmd.append(escape_string(*error_message, ESCAPE_ALL)); + // If it's an error, redirect the output of __fish_print_help to stderr + ios.push_back(std::make_shared(STDOUT_FILENO, STDERR_FILENO, false)); } - return out; -} - -/// Process and print for the specified builtin. If @c b is `sb_err`, also print the line -/// information. -/// -/// If @c b is the buffer representing standard error, and the help message is about to be printed -/// to an interactive screen, it may be shortened to fit the screen. -/// -void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, - output_stream_t &b) { - b.append(builtin_help_get(parser, streams, cmd)); + parser.eval(cmd, ios, TOP); + // ignore the exit status of __fish_print_help } /// Perform error reporting for encounter with unknown option. @@ -221,14 +206,14 @@ static int builtin_generic(parser_t &parser, io_streams_t &streams, wchar_t **ar if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } // Hackish - if we have no arguments other than the command, we are a "naked invocation" and we // just print help. if (argc == 1) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_INVALID_ARGS; } @@ -272,9 +257,8 @@ static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar int argc = builtin_count_args(argv); if (argc != 1) { - streams.err.append_format(BUILTIN_ERR_UNKNOWN, argv[0], argv[1]); - - builtin_print_help(parser, streams, argv[0], streams.err); + wcstring error_message = format_string(BUILTIN_ERR_UNKNOWN, argv[0], argv[1]); + builtin_print_help(parser, streams, argv[0], &error_message); return STATUS_INVALID_ARGS; } @@ -286,8 +270,8 @@ static int builtin_break_continue(parser_t &parser, io_streams_t &streams, wchar if (b->type() == FUNCTION_CALL) break; } if (!has_loop) { - streams.err.append_format(_(L"%ls: Not inside of loop\n"), argv[0]); - builtin_print_help(parser, streams, argv[0], streams.err); + wcstring error_message = format_string(_(L"%ls: Not inside of loop\n"), argv[0]); + builtin_print_help(parser, streams, argv[0], &error_message); return STATUS_CMD_ERROR; } @@ -455,7 +439,7 @@ proc_status_t builtin_run(parser_t &parser, int job_pgid, wchar_t **argv, io_str // follows the keyword by `-h` or `--help`. Since it isn't really a builtin command we need to // handle displaying help for it here. if (argv[1] && !argv[2] && parse_util_argument_is_help(argv[1]) && cmd_needs_help(argv[0])) { - builtin_print_help(parser, streams, argv[0], streams.out); + builtin_print_help(parser, streams, argv[0]); return proc_status_t::from_exit_code(STATUS_CMD_OK); } diff --git a/src/builtin.h b/src/builtin.h index a17fec0b5..359dd40be 100644 --- a/src/builtin.h +++ b/src/builtin.h @@ -89,7 +89,7 @@ const wchar_t *builtin_get_desc(const wcstring &b); wcstring builtin_help_get(parser_t &parser, const wchar_t *cmd); void builtin_print_help(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, - output_stream_t &b); + wcstring *error_message = nullptr); int builtin_count_args(const wchar_t *const *argv); void builtin_unknown_option(parser_t &parser, io_streams_t &streams, const wchar_t *cmd, diff --git a/src/builtin_argparse.cpp b/src/builtin_argparse.cpp index 4d482773e..2153a9379 100644 --- a/src/builtin_argparse.cpp +++ b/src/builtin_argparse.cpp @@ -699,7 +699,7 @@ int builtin_argparse(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_bg.cpp b/src/builtin_bg.cpp index 2e9b4e5ea..83b95faae 100644 --- a/src/builtin_bg.cpp +++ b/src/builtin_bg.cpp @@ -21,10 +21,10 @@ static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j) { assert(j != NULL); if (!j->wants_job_control()) { - streams.err.append_format( + wcstring error_message = format_string( _(L"%ls: Can't put job %d, '%ls' to background because it is not under job control\n"), L"bg", j->job_id, j->command_wcstr()); - builtin_print_help(parser, streams, L"bg", streams.err); + builtin_print_help(parser, streams, L"bg", &error_message); return STATUS_CMD_ERROR; } @@ -47,7 +47,7 @@ int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_bind.cpp b/src/builtin_bind.cpp index 477e15075..29c36c9a2 100644 --- a/src/builtin_bind.cpp +++ b/src/builtin_bind.cpp @@ -431,7 +431,7 @@ int builtin_bind_t::builtin_bind(parser_t &parser, io_streams_t &streams, wchar_ return STATUS_CMD_OK; } if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_block.cpp b/src/builtin_block.cpp index b42487dd3..00d70dfe0 100644 --- a/src/builtin_block.cpp +++ b/src/builtin_block.cpp @@ -81,7 +81,7 @@ int builtin_block(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_builtin.cpp b/src/builtin_builtin.cpp index dd8391c8a..e4ff40924 100644 --- a/src/builtin_builtin.cpp +++ b/src/builtin_builtin.cpp @@ -77,7 +77,7 @@ int builtin_builtin(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_cd.cpp b/src/builtin_cd.cpp index 7599e5cd3..31654747e 100644 --- a/src/builtin_cd.cpp +++ b/src/builtin_cd.cpp @@ -31,7 +31,7 @@ int builtin_cd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_command.cpp b/src/builtin_command.cpp index b13fdd390..396850528 100644 --- a/src/builtin_command.cpp +++ b/src/builtin_command.cpp @@ -84,13 +84,13 @@ int builtin_command(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } // Quiet implies find_path. if (!opts.find_path && !opts.all_paths && !opts.quiet) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_INVALID_ARGS; } diff --git a/src/builtin_commandline.cpp b/src/builtin_commandline.cpp index cf309b498..a8d3e112f 100644 --- a/src/builtin_commandline.cpp +++ b/src/builtin_commandline.cpp @@ -263,7 +263,7 @@ int builtin_commandline(parser_t &parser, io_streams_t &streams, wchar_t **argv) break; } case 'h': { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } case ':': { diff --git a/src/builtin_complete.cpp b/src/builtin_complete.cpp index 646c67ccd..7847c1b72 100644 --- a/src/builtin_complete.cpp +++ b/src/builtin_complete.cpp @@ -247,7 +247,7 @@ int builtin_complete(parser_t &parser, io_streams_t &streams, wchar_t **argv) { break; } case 'h': { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } case ':': { diff --git a/src/builtin_contains.cpp b/src/builtin_contains.cpp index 289b04645..c98c0bb1a 100644 --- a/src/builtin_contains.cpp +++ b/src/builtin_contains.cpp @@ -68,7 +68,7 @@ int builtin_contains(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_disown.cpp b/src/builtin_disown.cpp index f6c5965a3..aa1115883 100644 --- a/src/builtin_disown.cpp +++ b/src/builtin_disown.cpp @@ -52,7 +52,7 @@ int builtin_disown(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_emit.cpp b/src/builtin_emit.cpp index 8ef33a320..b837b30ab 100644 --- a/src/builtin_emit.cpp +++ b/src/builtin_emit.cpp @@ -21,7 +21,7 @@ int builtin_emit(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_exit.cpp b/src/builtin_exit.cpp index e747f10ae..6f8e9d58c 100644 --- a/src/builtin_exit.cpp +++ b/src/builtin_exit.cpp @@ -69,7 +69,7 @@ int builtin_exit(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_fg.cpp b/src/builtin_fg.cpp index c06d2a1a7..4a7808cab 100644 --- a/src/builtin_fg.cpp +++ b/src/builtin_fg.cpp @@ -31,7 +31,7 @@ int builtin_fg(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_functions.cpp b/src/builtin_functions.cpp index 8d8ccfe22..bca9eace2 100644 --- a/src/builtin_functions.cpp +++ b/src/builtin_functions.cpp @@ -298,7 +298,7 @@ int builtin_functions(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_history.cpp b/src/builtin_history.cpp index 70d68da61..adfac2063 100644 --- a/src/builtin_history.cpp +++ b/src/builtin_history.cpp @@ -212,7 +212,7 @@ int builtin_history(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_jobs.cpp b/src/builtin_jobs.cpp index 9a541a18b..fc080b73c 100644 --- a/src/builtin_jobs.cpp +++ b/src/builtin_jobs.cpp @@ -151,7 +151,7 @@ int builtin_jobs(parser_t &parser, io_streams_t &streams, wchar_t **argv) { break; } case 'h': { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } case ':': { diff --git a/src/builtin_math.cpp b/src/builtin_math.cpp index b2d1e11fb..d7a3c7fc8 100644 --- a/src/builtin_math.cpp +++ b/src/builtin_math.cpp @@ -243,7 +243,7 @@ int builtin_math(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_printf.cpp b/src/builtin_printf.cpp index d722dd4e7..dccdc5226 100644 --- a/src/builtin_printf.cpp +++ b/src/builtin_printf.cpp @@ -742,7 +742,7 @@ int builtin_printf(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_pwd.cpp b/src/builtin_pwd.cpp index 328cec5d7..8c0c7a71a 100644 --- a/src/builtin_pwd.cpp +++ b/src/builtin_pwd.cpp @@ -33,7 +33,7 @@ int builtin_pwd(parser_t &parser, io_streams_t &streams, wchar_t **argv) { resolve_symlinks = true; break; case 'h': - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; case '?': { builtin_unknown_option(parser, streams, cmd, argv[w.woptind - 1]); diff --git a/src/builtin_random.cpp b/src/builtin_random.cpp index f97a63914..3478810fd 100644 --- a/src/builtin_random.cpp +++ b/src/builtin_random.cpp @@ -38,7 +38,7 @@ int builtin_random(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_read.cpp b/src/builtin_read.cpp index 0fd657305..b2af119d6 100644 --- a/src/builtin_read.cpp +++ b/src/builtin_read.cpp @@ -431,7 +431,7 @@ int builtin_read(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_realpath.cpp b/src/builtin_realpath.cpp index d98a38f83..3a4ea7c89 100644 --- a/src/builtin_realpath.cpp +++ b/src/builtin_realpath.cpp @@ -28,13 +28,13 @@ int builtin_realpath(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } if (optind + 1 != argc) { // TODO: allow arbitrary args. `realpath *` should print many paths streams.err.append_format(BUILTIN_ERR_ARG_COUNT1, cmd, 1, argc - optind); - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_INVALID_ARGS; } diff --git a/src/builtin_return.cpp b/src/builtin_return.cpp index 0debc5ca5..11d8354c9 100644 --- a/src/builtin_return.cpp +++ b/src/builtin_return.cpp @@ -68,7 +68,7 @@ int builtin_return(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_set.cpp b/src/builtin_set.cpp index 5b8c06fea..6e3630676 100644 --- a/src/builtin_set.cpp +++ b/src/builtin_set.cpp @@ -807,7 +807,7 @@ int builtin_set(parser_t &parser, io_streams_t &streams, wchar_t **argv) { argc -= optind; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_set_color.cpp b/src/builtin_set_color.cpp index f84e33fac..0867cc684 100644 --- a/src/builtin_set_color.cpp +++ b/src/builtin_set_color.cpp @@ -109,7 +109,7 @@ int builtin_set_color(parser_t &parser, io_streams_t &streams, wchar_t **argv) { break; } case 'h': { - builtin_print_help(parser, streams, argv[0], streams.out); + builtin_print_help(parser, streams, argv[0]); return STATUS_CMD_OK; } case 'o': { diff --git a/src/builtin_source.cpp b/src/builtin_source.cpp index aec69ffef..0765053fb 100644 --- a/src/builtin_source.cpp +++ b/src/builtin_source.cpp @@ -33,7 +33,7 @@ int builtin_source(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_status.cpp b/src/builtin_status.cpp index 28b8e0489..cc974878d 100644 --- a/src/builtin_status.cpp +++ b/src/builtin_status.cpp @@ -280,7 +280,7 @@ int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { if (retval != STATUS_CMD_OK) return retval; if (opts.print_help) { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } diff --git a/src/builtin_string.cpp b/src/builtin_string.cpp index 6d1e9dc7f..f5cc6f971 100644 --- a/src/builtin_string.cpp +++ b/src/builtin_string.cpp @@ -1358,7 +1358,7 @@ int builtin_string(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } if (std::wcscmp(argv[1], L"-h") == 0 || std::wcscmp(argv[1], L"--help") == 0) { - builtin_print_help(parser, streams, L"string", streams.out); + builtin_print_help(parser, streams, L"string"); return STATUS_CMD_OK; } diff --git a/src/builtin_ulimit.cpp b/src/builtin_ulimit.cpp index 40fe6bfef..f50d9b4e9 100644 --- a/src/builtin_ulimit.cpp +++ b/src/builtin_ulimit.cpp @@ -239,7 +239,7 @@ int builtin_ulimit(parser_t &parser, io_streams_t &streams, wchar_t **argv) { } #endif case 'h': { - builtin_print_help(parser, streams, cmd, streams.out); + builtin_print_help(parser, streams, cmd); return STATUS_CMD_OK; } case ':': {