diff --git a/doc_src/status.txt b/doc_src/status.txt index df37d6102..45c264565 100644 --- a/doc_src/status.txt +++ b/doc_src/status.txt @@ -42,7 +42,7 @@ The following operations (sub-commands) are available: - `filename` prints the filename of the currently running script. Also `current-filename`, `-f` or `--current-filename`. -- `function` prints the name of the currently called function if able, when missing displays "Not a function". Also `current-function`, `-u` or `--current-function`. +- `function` prints the name of the currently called function if able, when missing displays "N/A". Also `current-function`, `-u` or `--current-function`. - `line-number` prints the line number of the currently running script. Also `current-line-number`, `-n` or `--current-line-number`. diff --git a/share/functions/fish_breakpoint_prompt.fish b/share/functions/fish_breakpoint_prompt.fish index b41f6e095..24d27d6d9 100644 --- a/share/functions/fish_breakpoint_prompt.fish +++ b/share/functions/fish_breakpoint_prompt.fish @@ -1,11 +1,11 @@ # Define the default debugging prompt command. function fish_breakpoint_prompt --description "A right prompt to be used when `breakpoint` is executed" set -l saved_status $status - set -l function (status current-function) - set -l line (status current-line-number) + set -l function (status -L0 function) + set -l line (status -L0 line-number) # At the moment we don't include the filename because, even if we truncate it, it makes the # prompt too long. - #set -l filename (status current-filename) + #set -l filename (status filename) #set -l prompt "$filename:$function:$line >" set -l prompt "$function:$line" if test $saved_status -ne 0 diff --git a/src/builtin_status.cpp b/src/builtin_status.cpp index 13d32471d..b646dd2c6 100644 --- a/src/builtin_status.cpp +++ b/src/builtin_status.cpp @@ -78,16 +78,16 @@ int job_control_str_to_mode(const wchar_t *mode, wchar_t *cmd, io_streams_t &str struct status_cmd_opts_t { bool print_help = false; - status_cmd_t status_cmd = STATUS_UNDEF; + int level = 1; int new_job_control_mode = -1; - bool breakpoint_context = false; + status_cmd_t status_cmd = STATUS_UNDEF; }; /// Note: Do not add new flags that represent subcommands. We're encouraging people to switch to /// the non-flag subcommand form. While these flags are deprecated they must be supported at /// least until fish 3.0 and possibly longer to avoid breaking everyones config.fish and other /// scripts. -static const wchar_t *short_options = L":Bcbilfnhj:t"; +static const wchar_t *short_options = L":L:cbilfnhj:t"; static const struct woption long_options[] = {{L"help", no_argument, NULL, 'h'}, {L"is-command-substitution", no_argument, NULL, 'c'}, {L"is-block", no_argument, NULL, 'b'}, @@ -98,12 +98,12 @@ static const struct woption long_options[] = {{L"help", no_argument, NULL, 'h'}, {L"is-no-job-control", no_argument, NULL, 3}, {L"filename", no_argument, NULL, 'f'}, {L"current-filename", no_argument, NULL, 'f'}, + {L"level", required_argument, NULL, 'L'}, {L"line", no_argument, NULL, 'n'}, {L"line-number", no_argument, NULL, 'n'}, {L"current-line-number", no_argument, NULL, 'n'}, {L"job-control", required_argument, NULL, 'j'}, {L"print-stack-trace", no_argument, NULL, 't'}, - {L"breakpoint", no_argument, NULL, 'B'}, {NULL, 0, NULL, 0}}; /// Remember the status subcommand and disallow selecting more than one status subcommand. @@ -149,8 +149,15 @@ static int parse_cmd_opts(status_cmd_opts_t &opts, int *optind, //!OCLINT(high } break; } - case 'B': { - opts.breakpoint_context = true; + case 'L': { + opts.level = fish_wcstoi(w.woptarg); + if (opts.level < 0 || errno == ERANGE) { + streams.err.append_format(_(L"%ls: Invalid level value '%ls'\n"), argv[0], w.woptarg); + return STATUS_INVALID_ARGS; + } else if (errno) { + streams.err.append_format(BUILTIN_ERR_NOT_NUMBER, argv[0], w.woptarg); + return STATUS_INVALID_ARGS; + } break; } case 'c': { @@ -301,20 +308,23 @@ int builtin_status(parser_t &parser, io_streams_t &streams, wchar_t **argv) { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) const wchar_t *fn = parser.current_filename(); - if (!fn) fn = _(L"Standard input"); + if (!fn) fn = _(L"stdin"); streams.out.append_format(L"%ls\n", fn); break; } case STATUS_FUNCTION: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) - const wchar_t *fn = parser.get_function_name(); + const wchar_t *fn = parser.get_function_name(opts.level); - if (!fn) fn = _(L"Not a function"); + if (!fn) fn = _(L"N/A"); streams.out.append_format(L"%ls\n", fn); break; } case STATUS_LINE_NUMBER: { CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) + // TBD is how to interpret the level argument when fetching the line number. + // See issue #4161. + // streams.out.append_format(L"%d\n", parser.get_lineno(opts.level)); streams.out.append_format(L"%d\n", parser.get_lineno()); break; } diff --git a/src/parser.cpp b/src/parser.cpp index 9ce07ec93..44edae94c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -434,14 +434,15 @@ void parser_t::stack_trace_internal(size_t block_idx, wcstring *buff) const { } /// Returns the name of the currently evaluated function if we are currently evaluating a function, -/// null otherwise. This is tested by moving down the block-scope-stack, checking every block if it -/// is of type FUNCTION_CALL. -const wchar_t *parser_t::is_function() const { +/// NULL otherwise. This is tested by moving down the block-scope-stack, checking every block if it +/// is of type FUNCTION_CALL. If the caller doesn't specify a starting position in the stack we +/// begin with the current block. +const wchar_t *parser_t::is_function(size_t idx) const { // PCA: Have to make this a string somehow. ASSERT_IS_MAIN_THREAD(); const wchar_t *result = NULL; - for (size_t block_idx = 0; block_idx < this->block_count(); block_idx++) { + for (size_t block_idx = idx; block_idx < this->block_count(); block_idx++) { const block_t *b = this->block_at_index(block_idx); if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) { const function_block_t *fb = static_cast(b); @@ -455,7 +456,37 @@ const wchar_t *parser_t::is_function() const { return result; } -const wchar_t *parser_t::get_function_name() { return this->is_function(); } +/// Return the function name for the specified stack frame. Default is zero (current frame). +/// The special value zero means the function frame immediately above the closest breakpoint frame. +const wchar_t *parser_t::get_function_name(int level) { + if (level == 0) { + // Return the function name for the level preceding the most recent breakpoint. If there + // isn't one return the function name for the current level. + int idx = 0; + for (const auto &b : block_stack) { + const enum block_type_t type = b->type(); + if (type == BREAKPOINT) { + return this->is_function(idx); + } + idx++; + } + return NULL; // couldn't find a breakpoint frame + } else if (level == 1) { + // Return the function name for the current level. + return this->is_function(); + } + + // Return the function name for the specific function stack frame. + int idx = 0; + for (const auto &b : block_stack) { + const enum block_type_t type = b->type(); + if (type == FUNCTION_CALL || type == FUNCTION_CALL_NO_SHADOW) { + if (--level == 0) return this->is_function(idx); + } + idx++; + } + return NULL; // couldn't find that function level +} int parser_t::get_lineno() const { int lineno = -1; diff --git a/src/parser.h b/src/parser.h index 852b9c9a6..b64983db1 100644 --- a/src/parser.h +++ b/src/parser.h @@ -217,7 +217,7 @@ class parser_t { /// Returns the name of the currently evaluated function if we are currently evaluating a /// function, null otherwise. This is tested by moving down the block-scope-stack, checking /// every block if it is of type FUNCTION_CALL. - const wchar_t *is_function() const; + const wchar_t *is_function(size_t idx=0) const; /// Helper for stack_trace(). void stack_trace_internal(size_t block_idx, wcstring *out) const; @@ -264,7 +264,7 @@ class parser_t { static void expand_argument_list(const wcstring &arg_src, expand_flags_t flags, std::vector *output); - /// Returns a string describing the current parser pisition in the format 'FILENAME (line + /// Returns a string describing the current parser position in the format 'FILENAME (line /// LINE_NUMBER): LINE'. Example: /// /// init.fish (line 127): ls|grep pancake @@ -307,8 +307,8 @@ class parser_t { /// Return a description of the given blocktype. const wchar_t *get_block_desc(int block) const; - /// Return the current function name. - const wchar_t *get_function_name(); + /// Return the function name for the specified stack frame. Default is one (current frame). + const wchar_t *get_function_name(int level=1); /// Removes a job. bool job_remove(job_t *job); diff --git a/tests/status.out b/tests/status.out index 989264396..f8ed9c107 100644 --- a/tests/status.out +++ b/tests/status.out @@ -1,3 +1,3 @@ -Not a function +N/A test_function test_function