implement status function when in a breakpoint

Another step to fixing #1310. This changes means that `status -L0
function` reports the correct function when inside a breakpoint.
This commit is contained in:
Kurtis Rader 2017-06-23 22:14:21 -07:00
parent 1bee66548a
commit 30368d5526
6 changed files with 64 additions and 23 deletions

View file

@ -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`. - `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`. - `line-number` prints the line number of the currently running script. Also `current-line-number`, `-n` or `--current-line-number`.

View file

@ -1,11 +1,11 @@
# Define the default debugging prompt command. # Define the default debugging prompt command.
function fish_breakpoint_prompt --description "A right prompt to be used when `breakpoint` is executed" function fish_breakpoint_prompt --description "A right prompt to be used when `breakpoint` is executed"
set -l saved_status $status set -l saved_status $status
set -l function (status current-function) set -l function (status -L0 function)
set -l line (status current-line-number) 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 # At the moment we don't include the filename because, even if we truncate it, it makes the
# prompt too long. # prompt too long.
#set -l filename (status current-filename) #set -l filename (status filename)
#set -l prompt "$filename:$function:$line >" #set -l prompt "$filename:$function:$line >"
set -l prompt "$function:$line" set -l prompt "$function:$line"
if test $saved_status -ne 0 if test $saved_status -ne 0

View file

@ -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 { struct status_cmd_opts_t {
bool print_help = false; bool print_help = false;
status_cmd_t status_cmd = STATUS_UNDEF; int level = 1;
int new_job_control_mode = -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 /// 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 /// 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 /// least until fish 3.0 and possibly longer to avoid breaking everyones config.fish and other
/// scripts. /// 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'}, static const struct woption long_options[] = {{L"help", no_argument, NULL, 'h'},
{L"is-command-substitution", no_argument, NULL, 'c'}, {L"is-command-substitution", no_argument, NULL, 'c'},
{L"is-block", no_argument, NULL, 'b'}, {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"is-no-job-control", no_argument, NULL, 3},
{L"filename", no_argument, NULL, 'f'}, {L"filename", no_argument, NULL, 'f'},
{L"current-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", no_argument, NULL, 'n'},
{L"line-number", no_argument, NULL, 'n'}, {L"line-number", no_argument, NULL, 'n'},
{L"current-line-number", no_argument, NULL, 'n'}, {L"current-line-number", no_argument, NULL, 'n'},
{L"job-control", required_argument, NULL, 'j'}, {L"job-control", required_argument, NULL, 'j'},
{L"print-stack-trace", no_argument, NULL, 't'}, {L"print-stack-trace", no_argument, NULL, 't'},
{L"breakpoint", no_argument, NULL, 'B'},
{NULL, 0, NULL, 0}}; {NULL, 0, NULL, 0}};
/// Remember the status subcommand and disallow selecting more than one status subcommand. /// 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; break;
} }
case 'B': { case 'L': {
opts.breakpoint_context = true; 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; break;
} }
case 'c': { 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) CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd)
const wchar_t *fn = parser.current_filename(); 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); streams.out.append_format(L"%ls\n", fn);
break; break;
} }
case STATUS_FUNCTION: { case STATUS_FUNCTION: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) 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); streams.out.append_format(L"%ls\n", fn);
break; break;
} }
case STATUS_LINE_NUMBER: { case STATUS_LINE_NUMBER: {
CHECK_FOR_UNEXPECTED_STATUS_ARGS(opts.status_cmd) 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()); streams.out.append_format(L"%d\n", parser.get_lineno());
break; break;
} }

View file

@ -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, /// 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 /// NULL otherwise. This is tested by moving down the block-scope-stack, checking every block if it
/// is of type FUNCTION_CALL. /// is of type FUNCTION_CALL. If the caller doesn't specify a starting position in the stack we
const wchar_t *parser_t::is_function() const { /// begin with the current block.
const wchar_t *parser_t::is_function(size_t idx) const {
// PCA: Have to make this a string somehow. // PCA: Have to make this a string somehow.
ASSERT_IS_MAIN_THREAD(); ASSERT_IS_MAIN_THREAD();
const wchar_t *result = NULL; 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); const block_t *b = this->block_at_index(block_idx);
if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) { if (b->type() == FUNCTION_CALL || b->type() == FUNCTION_CALL_NO_SHADOW) {
const function_block_t *fb = static_cast<const function_block_t *>(b); const function_block_t *fb = static_cast<const function_block_t *>(b);
@ -455,7 +456,37 @@ const wchar_t *parser_t::is_function() const {
return result; 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 parser_t::get_lineno() const {
int lineno = -1; int lineno = -1;

View file

@ -217,7 +217,7 @@ class parser_t {
/// Returns the name of the currently evaluated function if we are currently evaluating a /// 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 /// function, null otherwise. This is tested by moving down the block-scope-stack, checking
/// every block if it is of type FUNCTION_CALL. /// 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(). /// Helper for stack_trace().
void stack_trace_internal(size_t block_idx, wcstring *out) const; 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, static void expand_argument_list(const wcstring &arg_src, expand_flags_t flags,
std::vector<completion_t> *output); std::vector<completion_t> *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: /// LINE_NUMBER): LINE'. Example:
/// ///
/// init.fish (line 127): ls|grep pancake /// init.fish (line 127): ls|grep pancake
@ -307,8 +307,8 @@ class parser_t {
/// Return a description of the given blocktype. /// Return a description of the given blocktype.
const wchar_t *get_block_desc(int block) const; const wchar_t *get_block_desc(int block) const;
/// Return the current function name. /// Return the function name for the specified stack frame. Default is one (current frame).
const wchar_t *get_function_name(); const wchar_t *get_function_name(int level=1);
/// Removes a job. /// Removes a job.
bool job_remove(job_t *job); bool job_remove(job_t *job);

View file

@ -1,3 +1,3 @@
Not a function N/A
test_function test_function
test_function test_function