Make `break' auto-complete on the filename:linenumber of events.

Mdb supports the location of a breakpoint to be specified either
as a procedure name, or as a source location in the form of a
filename/linenumber pair. It has long had readline auto-complete
on procedure names; this diff adds auto-complete on source locations as well.

NEWS:
    Announce the new capability.

trace/mercury_trace_completion.{h,c}:
    Add a completer for break commands that is separate from the existing
    one that completes only procedure specifications. The new one completes
    on both those AND on the filename:linenumber of all the events in all
    the debuggable modules of the program.

    Move all the forward declarations of static functions to the top
    of mercury_trace_completion.c, and put them in the same order as
    the corresponding definitions.

trace/mercury_trace_internal.c:
    Use the new completer for the "break" command.

trace/mercury_trace_spy.c:
trace/mercury_trace_tables.c:
    Fix some documentation.

tests/debugger/completion.{inp,exp}:
    Update this test. Update the input, because in some cases, this diff
    causes the shortest unambiguous extension of the current line so far
    to become shorter, due to the addition of source locations to the set
    of strings being given to readline. And update the output to show
    the now-expanded set of matching strings at the points at which
    completion is taking place.
This commit is contained in:
Zoltan Somogyi
2015-12-18 12:07:16 +11:00
parent 18c9eecbce
commit b964f5802c
8 changed files with 270 additions and 75 deletions

3
NEWS
View File

@@ -306,6 +306,9 @@ Change to the Mercury debugger:
* Interactive queries are now supported on OS X.
* The "break" command can now auto-complete on the filename:linenumber pairs
of events, provided the Mercury system can access the readline library.
Changes to the extras distribution:
* We have added support for Unicode and other enhancements to the lex and

View File

@@ -82,17 +82,34 @@ func*completion.z func*completion.zz
b func*completion.z
1: + stop interface func completion.z/0-0 (det)
mdb>
completion. completion.sub2.
completion.sub1. completion.sub2.sub3.
completion.sub1. completion.sub2. completion.sub2.sub3.
completion.sub1.z1 completion.sub1.zp
completion. completion.sub1.m:15
completion.m:17 completion.sub1.m:17
completion.m:18 completion.sub2.
completion.m:21 completion.sub2.m:15
completion.m:24 completion.sub2.sub3.
completion.sub1. completion.sub2.sub3.m:13
completion.sub1. completion.sub2.m:15
completion.sub1.m:15 completion.sub2.sub3.
completion.sub1.m:17 completion.sub2.sub3.m:13
completion.sub2.
completion.sub1.m:15 completion.sub1.z1
completion.sub1.m:17 completion.sub1.zp
b completion.sub1.z1
2: + stop interface func completion.sub1.z1/0-0 (det)
mdb>
completion. completion.sub2.
completion.sub1. completion.sub2.sub3.
completion.sub1. completion.sub2. completion.sub2.sub3.
completion.sub2. completion.sub2.sub3.
completion. completion.sub1.m:15
completion.m:17 completion.sub1.m:17
completion.m:18 completion.sub2.
completion.m:21 completion.sub2.m:15
completion.m:24 completion.sub2.sub3.
completion.sub1. completion.sub2.sub3.m:13
completion.sub1. completion.sub2.m:15
completion.sub1.m:15 completion.sub2.sub3.
completion.sub1.m:17 completion.sub2.sub3.m:13
completion.sub2.
completion.sub2. completion.sub2.sub3.
completion.sub2.m:15 completion.sub2.sub3.m:13
completion.sub2.sub3.m:13 completion.sub2.sub3.zabc3
b completion.sub2.sub3.zabc3
3: + stop interface func completion.sub2.sub3.zabc3/0-0 (det)
mdb>

View File

@@ -8,7 +8,7 @@ form@_par@ --f@ lines 10
una@ex@
b za@
b func*complet@z@
b complet@s@1@1
b complet@s@2@s@
b complet@s@1@z1
b complet@s@2@s@z@
2d@e@l@
c

View File

@@ -36,21 +36,42 @@
#endif
#endif
/*
** Complete on NULL terminated array of strings.
** The strings will not be `free'd.
*/
static MR_CompleterList *MR_trace_string_array_completer(
const char *const *strings);
static char *MR_prepend_string(char *completion,
MR_CompleterData *data);
static char *MR_trace_completer_list_next(const char *word,
size_t word_len, MR_CompleterList **list);
static void MR_trace_free_completer_list(
MR_CompleterList *completer_list);
static char *MR_prepend_string(char *completion,
static char *MR_trace_sorted_array_completer_next(
const char *word, size_t word_length,
MR_CompleterData *data);
static MR_CompleterList *MR_trace_string_array_completer(
const char *const *strings);
static char *MR_trace_string_array_completer_next(
const char *word, size_t word_len,
MR_CompleterData *data);
static int MR_compare_source_file_lines(
const void *ptr1, const void *ptr2);
static void MR_insert_module_into_source_file_line_table(
const MR_ModuleLayout *module);
static char *MR_trace_filename_completer_next(const char *word,
size_t word_len, MR_CompleterData *);
static char *MR_trace_filter_completer_next(const char *word,
size_t word_len, MR_CompleterData *);
static void MR_trace_free_filter_completer_data(
MR_CompleterData data);
static char *MR_trace_map_completer_next(const char *word,
size_t word_len, MR_CompleterData *);
static void MR_trace_free_map_completer_data(
MR_CompleterData data);
/*---------------------------------------------------------------------------*/
/*
** Called by Readline when the user requests completion.
@@ -116,7 +137,7 @@ MR_trace_line_completer(const char *passed_word, int state)
}
if (command_end == insertion_point) {
/* We're completing the command itself. */
/* We are completing the command itself. */
int num_digits;
char *digits;
MR_CompleterList *command_completer;
@@ -149,7 +170,7 @@ MR_trace_line_completer(const char *passed_word, int state)
digits, MR_free_func, completer_list);
}
} else {
/* We're completing an argument of the command. */
/* We are completing an argument of the command. */
#define MR_MAX_COMMAND_NAME_LEN 256
char command[MR_MAX_COMMAND_NAME_LEN];
char *expanded_command;
@@ -248,7 +269,7 @@ MR_trace_completer_list_next(const char *word, size_t word_len,
while (1) {
current_completer = *list;
if (!current_completer) {
if (current_completer == NULL) {
return NULL;
}
result = (current_completer->MR_completer_func)(word, word_len,
@@ -296,9 +317,6 @@ typedef struct {
int MR_sorted_array_size;
} MR_SortedArrayCompleterData;
static char *MR_trace_sorted_array_completer_next(const char *word,
size_t word_length, MR_CompleterData *data);
MR_CompleterList *
MR_trace_sorted_array_completer(const char *word, size_t word_length,
int array_size, MR_GetSlotName get_slot_name)
@@ -364,8 +382,10 @@ typedef struct MR_StringArrayCompleterData_struct {
int MR_string_array_current_offset;
} MR_StringArrayCompleterData;
static char *MR_trace_string_array_completer_next(const char *word,
size_t word_len, MR_CompleterData *data);
/*
** Complete on a NULL terminated array of strings.
** The strings will not be `free'd.
*/
static MR_CompleterList *
MR_trace_string_array_completer(const char *const *strings)
@@ -403,10 +423,170 @@ MR_trace_string_array_completer_next(const char *word, size_t word_len,
}
/*---------------------------------------------------------------------------*/
/* Use Readline's filename completer. */
/*
** Complete on either a procedure specification, or a filename:linenumber pair.
**
** We construct the sorted array of filename:linenumber pairs by first
** adding all such pairs implied by the module layout structures of the Mercury
** modules that have debugging information to MR_source_file_lines,
** and then sorting that array. We cannot use the macros defined in
** mercury_array_util.h to keep the array sorted *during* the insertion
** process, because the array can gen very big (more than 150,000 entries
** for the Mercury compiler itself), and performing O(n) calls to
** MR_prepare_insert_into_sorted, whose complexity is itself O(n),
** would yield a quadratic algorithm.' We do the sorting using qsort,
** whose expected complexity is O(n log n). (I don't expect its worst case
** O(n^2) performance to ever occur in practice.)
**
** Since the MR_source_file_lines array is not sorted while we insert into it,
** we have no cheap way to search the entirety of the so-far filled in parts
** of the array to see whether the string we are about to add to it already
** occurs in it. However, we can stop most duplicates from ever getting into
** the array by simply checking if the string we are about to add is for
** the same line number in the same file as the previous string, and we do
** a final pass over the array after it is sorted, squeezing out the remaining
** duplicates, which are now guaranteed to be adjacent to each other.
*/
static char *MR_trace_filename_completer_next(const char *word,
size_t word_len, MR_CompleterData *);
#define INIT_SOURCE_FILE_LINE_TABLE_SIZE 10
static const char **MR_source_file_lines;
static unsigned MR_source_file_line_next = 0;
static unsigned MR_source_file_line_max = 0;
static MR_bool MR_source_file_lines_initialized = MR_FALSE;
#define INIT_SOURCE_FILE_LINE_CHARS_SIZE 100
#define LINE_NUM_MAX_CHARS 20
static char *MR_source_file_line_chars;
static unsigned MR_source_file_line_char_next = 0;
static unsigned MR_source_file_line_char_max = 0;
static void
MR_insert_module_into_source_file_line_table(const MR_ModuleLayout *module)
{
int num_files;
int cur_file;
num_files = module->MR_ml_filename_count;
for (cur_file = 0; cur_file < num_files; cur_file++) {
const MR_ModuleFileLayout *file;
MR_ConstString file_name;
int file_name_len;
int num_lines;
int cur_line;
file = module->MR_ml_module_file_layout[cur_file];
file_name = file->MR_mfl_filename;
file_name_len = strlen(file_name);
MR_ensure_big_enough(file_name_len + LINE_NUM_MAX_CHARS + 2,
MR_source_file_line_char, char,
INIT_SOURCE_FILE_LINE_CHARS_SIZE);
strcpy(MR_source_file_line_chars, file_name);
MR_source_file_line_chars[file_name_len] = ':';
num_lines = file->MR_mfl_label_count;
MR_ensure_big_enough(MR_source_file_line_next + num_lines,
MR_source_file_line, const char *,
INIT_SOURCE_FILE_LINE_TABLE_SIZE);
for (cur_line = 0; cur_line < num_lines; cur_line++) {
int line_num = file->MR_mfl_label_lineno[cur_line];
/*
** Almost all duplicates that end up in MR_source_file_lines
** get there from consecutive entries for the same line number
** in the same module file layout structure. Look for this
** relatively common case, and avoid adding the redundant entry
** to the array if we find it.
*/
if (cur_line > 0 &&
line_num == file->MR_mfl_label_lineno[cur_line - 1])
{
/*
** The string we would add would be the same as the string
** for the previous line number.
*/
continue;
}
snprintf(&MR_source_file_line_chars[file_name_len + 1],
LINE_NUM_MAX_CHARS, "%d", line_num);
MR_source_file_lines[MR_source_file_line_next] =
strdup(MR_source_file_line_chars);
MR_source_file_line_next++;
}
}
}
static int
MR_compare_source_file_lines(const void *ptr1, const void *ptr2)
{
char * const *sptr1 = (char * const *) ptr1;
char * const *sptr2 = (char * const *) ptr2;
return strcmp(*sptr1, *sptr2);
}
MR_CompleterList *
MR_trace_break_completer(const char *word, size_t word_len)
{
MR_CompleterList *completer_list;
MR_CompleterList *cur_completer_list;
completer_list = MR_trace_proc_spec_completer(word, word_len);
if (MR_strneq(word, "pred*", 5) || MR_strneq(word, "func*", 5)) {
/*
** These prefixes say that the breakpoint is on a named procedure,
** not on a filename/linenumber pair.
*/
return completer_list;
}
if (! MR_source_file_lines_initialized) {
int module_num;
int last;
int i;
for (module_num = 0; module_num < MR_module_info_next; module_num++) {
MR_insert_module_into_source_file_line_table(
MR_module_infos[module_num]);
}
qsort(MR_source_file_lines, MR_source_file_line_next,
sizeof(const char *), MR_compare_source_file_lines);
/*
** Squeeze out any duplicate entries, which are now guaranteed
** to be adjacent to each other.
*/
last = 0;
for (i = 1; i < MR_source_file_line_next; i++) {
if (MR_streq(MR_source_file_lines[i], MR_source_file_lines[last])) {
free(MR_source_file_lines[i]);
} else {
++last;
MR_source_file_lines[last] = MR_source_file_lines[i];
}
}
MR_source_file_line_next = last + 1;
MR_source_file_lines_initialized = MR_TRUE;
}
cur_completer_list = completer_list;
while (cur_completer_list->MR_completer_list_next != NULL) {
cur_completer_list = cur_completer_list->MR_completer_list_next;
}
cur_completer_list->MR_completer_list_next =
MR_trace_string_array_completer(MR_source_file_lines);
return completer_list;
}
/*---------------------------------------------------------------------------*/
/* Use Readline's filename completer. */
MR_CompleterList *
MR_trace_filename_completer(const char *word, size_t word_len)
@@ -440,10 +620,6 @@ typedef struct {
MR_CompleterList *MR_filter_list;
} MR_Filter_Completer_Data;
static char *MR_trace_filter_completer_next(const char *word,
size_t word_len, MR_CompleterData *);
static void MR_trace_free_filter_completer_data(MR_CompleterData data);
MR_CompleterList *
MR_trace_filter_completer(MR_CompleterFilter filter,
MR_CompleterData filter_data, MR_FreeCompleterData free_filter_data,
@@ -502,10 +678,6 @@ typedef struct {
MR_CompleterList *MR_map_list;
} MR_MapCompleterData;
static char *MR_trace_map_completer_next(const char *word,
size_t word_len, MR_CompleterData *);
static void MR_trace_free_map_completer_data(MR_CompleterData data);
MR_CompleterList *
MR_trace_map_completer(MR_Completer_Map map, MR_CompleterData map_data,
MR_FreeCompleterData free_data, MR_CompleterList *list)

View File

@@ -58,6 +58,10 @@ typedef MR_CompleterList *(* MR_MakeCompleter)(const char *word,
extern MR_CompleterList *MR_trace_null_completer(const char *word,
size_t word_len);
/* Complete on either a procedure specification, or filename:linenumber. */
extern MR_CompleterList *MR_trace_break_completer(const char *word,
size_t word_len);
/* Use Readline's filename completer. */
extern MR_CompleterList *MR_trace_filename_completer(const char *word,
size_t word_len);

View File

@@ -1556,7 +1556,7 @@ static const MR_TraceCmdTableEntry MR_trace_command_table[] =
NULL, MR_trace_null_completer },
{ "breakpoint", "break", MR_trace_cmd_break,
MR_trace_break_cmd_args, MR_trace_proc_spec_completer },
MR_trace_break_cmd_args, MR_trace_break_completer },
{ "breakpoint", "condition", MR_trace_cmd_condition,
NULL, MR_trace_null_completer },
{ "breakpoint", "ignore", MR_trace_cmd_ignore,

View File

@@ -186,14 +186,14 @@ static int
MR_compare_addr(const void *address1, const void *address2)
{
/*
** Note that we can't just compare the pointers, because
** because on a segmented architecture, that might
** only compare the segments, not the offsets (ANSI C
** doesn't require pointer comparisons to work unless
** the pointers point into the same array, which is not
** necessarily going to be the case here).
** So instead we need to cast the pointers to integers
** and compare the integers.
** Note that we can't just compare the pointers, because on a
** segmented architecture, that might only compare the segments,
** not the offsets (ANSI C doesn't require pointer comparisons to work
** unless the pointers point into the same array, which is not necessarily
** going to be the case here).
**
** So instead we need to cast the pointers to integers and then
** compare the integers.
*/
MR_Unsigned num1 = (MR_Unsigned) address1;
MR_Unsigned num2 = (MR_Unsigned) address2;
@@ -222,7 +222,7 @@ MR_search_spy_table_for_proc(const MR_ProcLayout *entry)
/*
** Return the index of the entry in MR_spied_labels whose MR_sl_label field
** is label, or a negative number if absent.
** is equal to the label argument, or a negative number if absent.
*/
static int
@@ -242,8 +242,8 @@ MR_search_spy_table_for_label(const MR_LabelLayout *label)
/*
** Return the index of the entry in MR_spied_user_events whose
** MR_sue_user_event_name field is user_event_name, or a negative number
** if absent.
** MR_sue_user_event_name field is equal to the user_event_name argument,
** or a negative number if absent.
*/
static int
@@ -264,8 +264,8 @@ MR_search_spy_table_for_user_event_name(const char *user_event_name)
/*
** Return the index of the entry in MR_spied_user_event_sets whose
** MR_sues_user_event_set field is user_event_set, or a negative number
** if absent.
** MR_sues_user_event_set field is equal to the user_event_set argument,
** or a negative number if absent.
*/
static int
@@ -770,7 +770,7 @@ MR_add_line_spy_point(MR_SpyAction action, MR_SpyIgnore_When ignore_when,
MR_fatal_error("MR_add_line_spy_point: num_line_matches != 0");
}
/* there were no matching labels */
/* There were no matching labels. */
#ifdef MR_HAVE_A_SNPRINTF
if (num_file_matches == 0) {
snprintf(MR_error_msg_buf, MR_ERROR_MSG_BUF_SIZE,
@@ -781,7 +781,7 @@ MR_add_line_spy_point(MR_SpyAction action, MR_SpyIgnore_When ignore_when,
linenumber, filename);
}
#else
/* not absolutely safe, but the risk of overflow is minimal */
/* Not absolutely safe, but the risk of overflow is minimal. */
if (num_file_matches == 0) {
sprintf(MR_error_msg_buf,
"there is no debuggable source file named %s", filename);
@@ -944,12 +944,12 @@ MR_add_spy_point_print_list_start(int point_slot, MR_SpyPrintList print_list)
return;
}
/* find the last node in print_list */
/* Find the last node in print_list. */
while (list->MR_pl_next != NULL) {
list = list->MR_pl_next;
}
/* add the existing spy_print_list at the end of print_list */
/* Add the existing spy_print_list at the end of print_list. */
list->MR_pl_next = MR_spy_points[point_slot]->MR_spy_print_list;
MR_spy_points[point_slot]->MR_spy_print_list = print_list;
}
@@ -965,12 +965,12 @@ MR_add_spy_point_print_list_end(int point_slot, MR_SpyPrintList print_list)
return;
}
/* find the last node in print_list */
/* Find the last node in print_list. */
while (list->MR_pl_next != NULL) {
list = list->MR_pl_next;
}
/* add the print_list at the end of the existing spy_print_list */
/* Add the print_list at the end of the existing spy_print_list. */
list->MR_pl_next = print_list;
}
@@ -1046,7 +1046,7 @@ MR_delete_spy_point(int point_table_slot)
MR_spy_points[point_table_slot]->MR_spy_exists = MR_FALSE;
MR_delete_spy_print_list(point->MR_spy_print_list);
/* in case it gets deleted again */
/* In case it gets deleted again. */
point->MR_spy_print_list = NULL;
if (point->MR_spy_cond != NULL) {
@@ -1054,7 +1054,7 @@ MR_delete_spy_point(int point_table_slot)
MR_free(point->MR_spy_cond->MR_cond_what_string);
MR_free(point->MR_spy_cond);
/* in case it gets deleted again */
/* In case it gets deleted again. */
point->MR_spy_cond = NULL;
}
@@ -1080,8 +1080,7 @@ MR_delete_spy_point(int point_table_slot)
MR_spied_label_next = label_slot;
} else {
/*
** Remove the spy point from the spied proc table list
** for its proc.
** Remove the spy point from the spied proc table list for its proc.
*/
proc_table_slot = MR_search_spy_table_for_proc(point->MR_spy_proc);

View File

@@ -109,10 +109,10 @@ typedef struct {
int MR_unambiguous_matching_module;
/*
** Length of the part of the word to skip when testing for module
** qualified completions in the current module, zero if we
** shouldn't test for module qualified completions in the
** current module.
** Length of the part of the word to skip when testing for
** module qualified completions in the current module,
** zero if we shouldn't test for module qualified completions
** in the current module.
*/
int MR_complete_word_matches_module;
int MR_complete_current_module;
@@ -378,7 +378,7 @@ MR_process_line_layouts(const MR_ModuleFileLayout *file_layout, int line,
}
/*
** ... and the call *all* such labels.
** ... and then do a callback for *all* such labels.
*/
while (k < file_layout->MR_mfl_label_count
@@ -404,7 +404,7 @@ MR_dump_module_tables(FILE *fp, MR_bool separate, MR_bool uci,
if (module_name != NULL) {
module = MR_search_module_info_by_unique_name(fp, module_name);
if (module == NULL) {
/* The error message has already been printed */
/* The error message has already been printed. */
return;
}
} else {
@@ -449,7 +449,7 @@ MR_dump_module_procs(FILE *fp, const char *name)
module = MR_search_module_info_by_unique_name(fp, name);
if (module == NULL) {
/* The error message has already been printed */
/* The error message has already been printed. */
return;
}
@@ -621,7 +621,8 @@ typedef struct {
((functor1).MR_functor_arity - (functor2).MR_functor_arity)
#define MR_functor_compare_type_module(functor1, functor2) \
strcmp((functor1).MR_functor_type_module, (functor2).MR_functor_type_module)
strcmp((functor1).MR_functor_type_module, \
(functor2).MR_functor_type_module)
#define MR_functor_compare_type_name(functor1, functor2) \
strcmp((functor1).MR_functor_type_name, (functor2).MR_functor_type_name)
@@ -1471,11 +1472,6 @@ MR_trace_proc_spec_completer(const char *word, size_t word_len)
data->MR_complete_pf = MR_FUNCTION;
word += 5;
} else {
/*
** We don't complete on the names of special (unify, index compare,
** or init) predicates.
*/
data->MR_complete_pf = -1;
}
@@ -1490,7 +1486,7 @@ MR_trace_proc_spec_completer(const char *word, size_t word_len)
/*
** Handle the case where the word matches the first part of a module name.
** If the word unambiguously determines the module name we want to return
** If the word unambiguously determines the module name, we want to return
** module qualified completions for all the procedures in that module.
** Otherwise, we just complete on the names of all of the matching modules
** and unqualified procedure names.
@@ -1500,6 +1496,9 @@ MR_trace_proc_spec_completer(const char *word, size_t word_len)
** as well as all procedures whose unqualified names begin with `f'.
** Given word to complete `foo.' and modules `foo' and `foo.bar' we want to
** return `foo.bar.' and all the procedures in module `foo' as completions.
**
** We don't complete on the names of special (unify, index or compare)
** predicates.
*/
MR_bsearch(MR_module_info_next, slot, found,
@@ -1546,7 +1545,8 @@ try_completion:
if (data->MR_complete_current_module == -1 ||
data->MR_complete_current_proc == -1 ||
data->MR_complete_current_proc >=
MR_module_infos[data->MR_complete_current_module]->MR_ml_proc_count)
MR_module_infos[data->MR_complete_current_module]->
MR_ml_proc_count)
{
/*
** Move on to the next module.