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`.
- `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`.

View file

@ -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

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 {
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;
}

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,
/// 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<const function_block_t *>(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;

View file

@ -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<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:
///
/// 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);

View file

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