[m-rev.] for review: mdb command line completion
Simon Taylor
stayl at cs.mu.OZ.AU
Mon Mar 4 18:32:03 AEDT 2002
Estimated hours taken: 40
Branches: main
Add support for command line completion to mdb.
NEWS:
Document the change.
trace/mercury_trace_completion.{c,h}:
Define the framework for completion.
Examine command lines to determine which completers to use.
trace/mercury_trace_readline.c:
Tell Readline to use our completer.
trace/mercury_trace_alias.{c,h}:
trace/mercury_trace_help.{c,h}:
trace/mercury_trace_internal.{c,h}:
trace/mercury_trace_tables.{c,h}:
trace/mercury_trace_vars.{c,h}:
Define context-specific completers.
trace/mercury_trace_help.c:
Record all help topics in an array for use by the completer.
trace/mercury_trace_internal.c:
Add completion information to the list of commands.
Add a function MR_trace_command_completion_info to access
that information.
trace/Mmakefile:
Add mercury_trace_completion.{c,h}.
runtime/mercury_array_macros.h:
Define a macro MR_find_first_match, which is like MR_bsearch
except that it finds the first match, not an arbitary match.
runtime/mercury_memory.c:
Handle NULL pointers in MR_copy_string();
runtime/mercury_memory.h:
Add a macro MR_free_func which returns the address of free().
Used where it is necessary to pass the address of MR_free().
Index: NEWS
===================================================================
RCS file: /home/mercury1/repository/mercury/NEWS,v
retrieving revision 1.245
diff -u -u -r1.245 NEWS
--- NEWS 24 Feb 2002 11:53:16 -0000 1.245
+++ NEWS 3 Mar 2002 17:09:05 -0000
@@ -201,6 +201,9 @@
* The debugger can now print higher order values.
+* The debugger can now perform command line completion when compiled
+ with GNU Readline support enabled.
+
* We've added a 'view' command to `mdb', which opens a `vim' window and
in it displays the current source location, updated at each event. This
requires X11 and a version of `vim' with the `clientserver' feature
Index: runtime/mercury_array_macros.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_array_macros.h,v
retrieving revision 1.9
diff -u -u -r1.9 mercury_array_macros.h
--- runtime/mercury_array_macros.h 18 Feb 2002 07:01:13 -0000 1.9
+++ runtime/mercury_array_macros.h 3 Mar 2002 17:06:21 -0000
@@ -172,6 +172,29 @@
} while(0)
/*
+** MR_find_first_match(int next, int& element, MR_bool& found, COMPARE)
+**
+** Given a sorted array, this macro finds the first element in the array
+** for which `COMPARE' is zero (MR_bsearch finds an arbitrary element).
+** Otherwise, the parameters and behaviour are the same as for MR_bsearch.
+*/
+
+#define MR_find_first_match(next, element, found, COMPARE) \
+ do { \
+ MR_bsearch((next), (element), (found), (COMPARE)); \
+ if (found) { \
+ while ((element) > 0) { \
+ int diff; \
+ (element)--; \
+ if ((COMPARE) != 0) { \
+ (element)++; \
+ break; \
+ } \
+ } \
+ } \
+ } while (0)
+
+/*
** MR_prepare_insert_into_sorted(array[], int& next, int& element, COMPARE)
**
** Given a sorted array of `items', this prepares for the insertion of a
Index: runtime/mercury_memory.c
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_memory.c,v
retrieving revision 1.29
diff -u -u -r1.29 mercury_memory.c
--- runtime/mercury_memory.c 18 Feb 2002 07:01:18 -0000 1.29
+++ runtime/mercury_memory.c 3 Mar 2002 17:06:21 -0000
@@ -269,10 +269,14 @@
int len;
char *copy;
- len = strlen(s);
- copy = MR_malloc(len + 1);
- strcpy(copy, s);
- return copy;
+ if (s == NULL) {
+ return NULL;
+ } else {
+ len = strlen(s);
+ copy = MR_malloc(len + 1);
+ strcpy(copy, s);
+ return copy;
+ }
}
/*---------------------------------------------------------------------------*/
Index: runtime/mercury_memory.h
===================================================================
RCS file: /home/mercury1/repository/mercury/runtime/mercury_memory.h,v
retrieving revision 1.16
diff -u -u -r1.16 mercury_memory.h
--- runtime/mercury_memory.h 18 Feb 2002 07:01:18 -0000 1.16
+++ runtime/mercury_memory.h 3 Mar 2002 17:06:21 -0000
@@ -80,6 +80,7 @@
extern void *MR_realloc(void *old, size_t n);
#define MR_free(ptr) free(ptr)
+#define MR_free_func free
#define MR_NEW(type) \
((type *) MR_malloc(sizeof(type)))
Index: trace/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/Mmakefile,v
retrieving revision 1.25
diff -u -u -r1.25 Mmakefile
--- trace/Mmakefile 22 Feb 2002 07:02:31 -0000 1.25
+++ trace/Mmakefile 3 Mar 2002 17:06:21 -0000
@@ -41,6 +41,7 @@
mercury_trace.h \
mercury_trace_alias.h \
mercury_trace_browse.h \
+ mercury_trace_completion.h \
mercury_trace_declarative.h \
mercury_trace_external.h \
mercury_trace_help.h \
@@ -57,6 +58,7 @@
mercury_trace.c \
mercury_trace_alias.c \
mercury_trace_browse.c \
+ mercury_trace_completion.c \
mercury_trace_declarative.c \
mercury_trace_external.c \
mercury_trace_help.c \
Index: trace/mercury_trace_alias.c
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_alias.c,v
retrieving revision 1.5
diff -u -u -r1.5 mercury_trace_alias.c
--- trace/mercury_trace_alias.c 18 Feb 2002 07:01:28 -0000 1.5
+++ trace/mercury_trace_alias.c 4 Mar 2002 05:27:27 -0000
@@ -25,6 +25,9 @@
static void MR_trace_print_alias_num(FILE *fp, int slot,
MR_bool mdb_command_format);
+static char * MR_trace_get_alias_slot_name(int slot);
+static MR_bool MR_trace_filter_alias_completions(const char *word,
+ MR_Completer_Data *data);
void
MR_trace_add_alias(char *name, char **words, int word_count)
@@ -154,3 +157,28 @@
fprintf(fp, "\n");
}
+
+MR_Completer_List *
+MR_trace_alias_completer(const char *word, size_t word_length)
+{
+ /*
+ ** Remove "EMPTY" and "NUMBER" from the possible matches.
+ */
+ return MR_trace_filter_completer(MR_trace_filter_alias_completions,
+ NULL, MR_trace_no_free,
+ MR_trace_sorted_array_completer(word, word_length,
+ MR_alias_record_next, MR_trace_get_alias_slot_name));
+}
+
+static char *
+MR_trace_get_alias_slot_name(int slot)
+{
+ return MR_alias_records[slot].MR_alias_name;
+}
+
+static MR_bool
+MR_trace_filter_alias_completions(const char *word, MR_Completer_Data *data)
+{
+ return (MR_strdiff(word, "EMPTY") && MR_strdiff(word, "NUMBER"));
+}
+
Index: trace/mercury_trace_alias.h
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_alias.h,v
retrieving revision 1.4
diff -u -u -r1.4 mercury_trace_alias.h
--- trace/mercury_trace_alias.h 18 Feb 2002 07:01:28 -0000 1.4
+++ trace/mercury_trace_alias.h 3 Mar 2002 17:06:21 -0000
@@ -16,6 +16,8 @@
#include "mercury_std.h" /* for MR_bool */
#include <stdio.h>
+#include "mercury_trace_completion.h"
+
typedef struct {
char *MR_alias_name;
char **MR_alias_words;
@@ -68,5 +70,8 @@
extern void MR_trace_print_all_aliases(FILE *fp,
MR_bool mdb_command_format);
+/* A Readline completer for aliases. */
+extern MR_Completer_List *MR_trace_alias_completer(const char *word,
+ size_t word_length);
#endif /* MERCURY_TRACE_ALIAS_H */
Index: trace/mercury_trace_completion.c
===================================================================
RCS file: trace/mercury_trace_completion.c
diff -N trace/mercury_trace_completion.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ trace/mercury_trace_completion.c 4 Mar 2002 07:19:48 -0000
@@ -0,0 +1,543 @@
+/*
+** Copyright (C) 2002 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** mercury_trace_completion.c
+**
+** Main author: stayl
+**
+** Command line completion for mdb.
+*/
+
+#include "mercury_memory.h"
+#include "mercury_std.h"
+#include "mercury_array_macros.h"
+#include "mercury_trace_completion.h"
+#include "mercury_trace_internal.h"
+#include "mercury_trace_alias.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef MR_NO_USE_READLINE
+ #ifdef MR_HAVE_READLINE_READLINE
+ #include <readline/readline.h>
+ #else
+ extern char *rl_line_buffer;
+ extern int rl_point;
+ extern char *filename_completion_function(char *word, int state);
+ #endif
+#endif
+
+#define MR_MAX_COMMAND_LEN 32
+
+typedef struct MR_String_Array_Completer_Data_struct {
+ int offset;
+ char **strings;
+} MR_String_Array_Completer_Data;
+
+typedef struct {
+ MR_Get_Slot_Name MR_sorted_array_get_slot_name;
+ int MR_sorted_array_offset;
+ int MR_sorted_array_size;
+} MR_Sorted_Array_Completer_Data;
+
+typedef struct {
+ MR_Completer_Filter MR_filter_func;
+ MR_Completer_Data MR_filter_data;
+ MR_Free_Completer_Data MR_filter_free_data;
+ MR_Completer_List * MR_filter_list;
+} MR_Filter_Completer_Data;
+
+typedef struct {
+ MR_Completer_Map MR_map_func;
+ MR_Completer_Data MR_map_data;
+ MR_Free_Completer_Data MR_map_free_data;
+ MR_Completer_List * MR_map_list;
+} MR_Map_Completer_Data;
+
+ /*
+ ** Complete on NULL terminated array of strings.
+ ** The strings will not be `free'd.
+ */
+static MR_Completer_List *MR_trace_string_array_completer(
+ const char *const *strings);
+
+static char *MR_trace_completer_list_next(const char *word,
+ size_t word_len, MR_Completer_List **list);
+static void MR_trace_free_completer_list(MR_Completer_List *completer_list);
+
+static char *MR_trace_sorted_array_completer_next(const char *word,
+ size_t word_length, MR_Completer_Data *data);
+
+static char *MR_trace_filter_completer_next(const char *word,
+ size_t word_len, MR_Completer_Data *);
+static void MR_trace_free_filter_completer_data(MR_Completer_Data data);
+
+static char *MR_trace_map_completer_next(const char *word,
+ size_t word_len, MR_Completer_Data *);
+static void MR_trace_free_map_completer_data(MR_Completer_Data data);
+
+static char *MR_trace_string_array_completer_next(const char *word,
+ size_t word_len, MR_Completer_Data *data);
+
+static char *MR_trace_filename_completer_next(const char *word,
+ size_t word_len, MR_Completer_Data *);
+
+static char *MR_prepend_string(char *completion, MR_Completer_Data *data);
+
+char *MR_trace_line_completer(const char *passed_word, int state)
+{
+#ifndef MR_NO_USE_READLINE
+ static MR_Completer_List *completer_list;
+ static char *word;
+ static size_t word_len;
+ char *completion;
+
+ /*
+ ** If `state' is 0, this is the first call for this `word',
+ ** so set up the list of completers.
+ */
+ if (state == 0) {
+ char *line;
+ char *command_end;
+ char *command_start;
+ char *insertion_point;
+ char *semicolon;
+
+ MR_trace_free_completer_list(completer_list);
+ completer_list = NULL;
+ if (word != NULL) {
+ MR_free(word);
+ }
+
+ line = rl_line_buffer;
+ insertion_point = rl_line_buffer + rl_point;
+
+ /*
+ ** There may be multiple commands in the line.
+ ** Skip to the one we are trying to complete.
+ */
+ semicolon = strchr(line, ';');
+ while (semicolon != NULL && semicolon < insertion_point) {
+ line = semicolon + 1;
+ semicolon = strchr(line, ';');
+ }
+
+ /* Skip space or a number at the beginning of the command. */
+ while (line < insertion_point &&
+ (MR_isspace(*line) || MR_isdigit(*line)))
+ {
+ line++;
+ }
+
+ /* Find the end of the command. */
+ command_start = line;
+ command_end = line;
+ while (command_end < insertion_point && !MR_isspace(*command_end)) {
+ command_end++;
+ }
+
+ if (command_end == insertion_point) {
+ /*
+ ** We're completing the command itself.
+ */
+
+ int num_digits;
+ char *digits;
+ MR_Completer_List *command_completer;
+ MR_Completer_List *alias_completer;
+
+ /*
+ ** Strip off any number preceding the command
+ ** (it will need to be added back later).
+ */
+ num_digits = 0;
+ while (MR_isdigit(passed_word[num_digits])) {
+ num_digits++;
+ }
+ word = MR_copy_string(passed_word + num_digits);
+ word_len = strlen(word);
+
+ /*
+ ** Set up completers for commands and aliases.
+ */
+ command_completer = MR_trace_command_completer(word, word_len);
+ alias_completer = MR_trace_alias_completer(word, word_len);
+
+ completer_list = command_completer;
+ completer_list->MR_completer_list_next = alias_completer;
+
+ /*
+ ** Add back the preceding number to the completions.
+ */
+ if (num_digits != 0) {
+ digits = MR_malloc(num_digits + 1);
+ strncpy(digits, passed_word, num_digits);
+ digits[num_digits] = '\0';
+ completer_list =
+ MR_trace_map_completer(MR_prepend_string,
+ digits, MR_free_func, completer_list);
+ }
+ } else {
+ /*
+ ** We're completing an argument of the command.
+ */
+
+ char command[MR_MAX_COMMAND_LEN];
+ char *expanded_command;
+ int command_len;
+ char **words;
+ int word_count;
+ MR_Make_Completer command_completer;
+ const char *const *command_fixed_args;
+ MR_Completer_List *arg_completer;
+
+ command_len = command_end - command_start;
+ if (command_len >= MR_MAX_COMMAND_LEN) {
+ /* The string is too long to be a command. */
+ return NULL;
+ } else {
+ strncpy(command, command_start, command_len);
+ command[command_len] = '\0';
+ }
+
+ /* Expand aliases. */
+ if (MR_trace_lookup_alias(command, &words, &word_count)) {
+ if (word_count == 0) {
+ return NULL;
+ } else {
+ expanded_command = words[0];
+ }
+ } else {
+ expanded_command = command;
+ }
+
+ if (! MR_trace_command_completion_info(expanded_command,
+ &command_completer, &command_fixed_args))
+ {
+ return NULL;
+ }
+
+ /* Set up a completer for the fixed argument strings. */
+ completer_list = NULL;
+ if (command_fixed_args != NULL) {
+ completer_list = MR_trace_string_array_completer(
+ command_fixed_args);
+ }
+
+ word = MR_copy_string(passed_word);
+ word_len = strlen(word);
+
+ /* Set up a completer for the non-fixed argument strings. */
+ arg_completer = (*command_completer)(word, word_len);
+ if (completer_list == NULL) {
+ completer_list = arg_completer;
+ } else {
+ completer_list->MR_completer_list_next = arg_completer;
+ }
+ }
+ }
+ completion = MR_trace_completer_list_next(word, word_len, &completer_list);
+ if (completion == NULL) {
+ MR_trace_free_completer_list(completer_list);
+ MR_free(word);
+ word = NULL;
+ }
+ return completion;
+#else
+ return NULL;
+#endif
+}
+
+static char *
+MR_prepend_string(char *string, MR_Completer_Data *data)
+{
+ char *string_to_prepend;
+ int string_to_prepend_len;
+ char *result;
+
+ string_to_prepend = (char *) *data;
+ string_to_prepend_len = strlen(string_to_prepend);
+ result = MR_malloc(string_to_prepend_len + strlen(string) + 1);
+ strcpy(result, string_to_prepend);
+ strcpy(result + string_to_prepend_len, string);
+ MR_free(string);
+ return result;
+}
+
+static char *
+MR_trace_completer_list_next(const char *word, size_t word_len,
+ MR_Completer_List **list)
+{
+ MR_Completer_List *current_completer;
+ char *result;
+
+ if (list == NULL) {
+ return NULL;
+ }
+
+ while (1) {
+ current_completer = *list;
+ if (!current_completer) {
+ return NULL;
+ }
+ result = (current_completer->MR_completer_func)(word, word_len,
+ &(current_completer->MR_completer_func_data));
+ if (result != NULL) {
+ return result;
+ } else {
+ *list = current_completer->MR_completer_list_next;
+ (current_completer->MR_free_completer_func_data)(
+ current_completer->MR_completer_func_data);
+ MR_free(current_completer);
+ }
+ }
+}
+
+static void
+MR_trace_free_completer_list(MR_Completer_List *completer_list)
+{
+ MR_Completer_List *tmp_list;
+ while (completer_list != NULL) {
+ tmp_list = completer_list;
+ completer_list = completer_list->MR_completer_list_next;
+ (tmp_list->MR_free_completer_func_data)(
+ tmp_list->MR_completer_func_data);
+ MR_free(tmp_list);
+ }
+}
+
+MR_Completer_List *
+MR_trace_null_completer(const char *word, size_t word_len)
+{
+ return NULL;
+}
+
+MR_Completer_List *
+MR_trace_sorted_array_completer(const char *word, size_t word_length,
+ int array_size, MR_Get_Slot_Name get_slot_name)
+{
+ MR_Completer_List *completer;
+ MR_bool found;
+ int slot;
+ MR_Sorted_Array_Completer_Data *data;
+
+ /*
+ ** Find the slot containing the first possible match,
+ ** optimizing for the common case where we are finding
+ ** all elements in the array.
+ */
+ if (word_length == 0) {
+ found = (array_size != 0);
+ slot = 0;
+ } else {
+ MR_find_first_match(array_size, slot, found,
+ strncmp(get_slot_name(slot), word, word_length));
+ }
+
+ if (found) {
+ data = MR_NEW(MR_Sorted_Array_Completer_Data);
+ data->MR_sorted_array_get_slot_name = get_slot_name;
+ data->MR_sorted_array_offset = slot;
+ data->MR_sorted_array_size = array_size;
+ (completer) = MR_new_completer_elem(
+ MR_trace_sorted_array_completer_next,
+ (MR_Completer_Data) data, MR_free_func);
+ } else {
+ (completer) = NULL;
+ }
+ return completer;
+}
+
+static char *
+MR_trace_sorted_array_completer_next(const char *word,
+ size_t word_length, MR_Completer_Data *completer_data)
+{
+ MR_Sorted_Array_Completer_Data *data;
+ char *completion;
+
+ data = (MR_Sorted_Array_Completer_Data *) *completer_data;
+
+ if (data->MR_sorted_array_offset < data->MR_sorted_array_size) {
+ completion = data->MR_sorted_array_get_slot_name(
+ data->MR_sorted_array_offset);
+ if (MR_strneq(completion, word, word_length)) {
+ data->MR_sorted_array_offset++;
+ return MR_copy_string(completion);
+ } else {
+ return NULL;
+ }
+ } else {
+ return NULL;
+ }
+}
+
+static MR_Completer_List *
+MR_trace_string_array_completer(const char *const *strings)
+{
+ MR_String_Array_Completer_Data *data;
+ data = MR_NEW(MR_String_Array_Completer_Data);
+ data->offset = 0;
+ data->strings = (char **) strings;
+ return MR_new_completer_elem(&MR_trace_string_array_completer_next,
+ (MR_Completer_Data) data, MR_free_func);
+}
+
+static char *
+MR_trace_string_array_completer_next(const char *word, size_t word_len,
+ MR_Completer_Data *data)
+{
+ MR_String_Array_Completer_Data *completer_data;
+ char *result;
+
+ completer_data = (MR_String_Array_Completer_Data *) *data;
+
+ while (1) {
+ result = completer_data->strings[completer_data->offset];
+ completer_data->offset++;
+ if (result == NULL) {
+ return NULL;
+ } else {
+ if (strncmp(result, word, word_len) == 0) {
+ return MR_copy_string(result);
+ }
+ }
+ }
+}
+
+MR_Completer_List *
+MR_trace_filename_completer(const char *word, size_t word_len)
+{
+ return MR_new_completer_elem(&MR_trace_filename_completer_next,
+ (MR_Completer_Data) 0, MR_trace_no_free);
+}
+
+
+static char *
+MR_trace_filename_completer_next(const char *word, size_t word_len,
+ MR_Completer_Data *data)
+{
+ int state;
+ state = (int) *data;
+ *data = (MR_Completer_Data) 1;
+ return filename_completion_function((char *) word, state);
+}
+
+MR_Completer_List *
+MR_trace_filter_completer(MR_Completer_Filter filter,
+ MR_Completer_Data filter_data,
+ MR_Free_Completer_Data free_filter_data,
+ MR_Completer_List *list)
+{
+ MR_Filter_Completer_Data *data;
+
+ data = MR_NEW(MR_Filter_Completer_Data);
+ data->MR_filter_func = filter;
+ data->MR_filter_data = filter_data;
+ data->MR_filter_free_data = free_filter_data;
+ data->MR_filter_list = list;
+ return MR_new_completer_elem(MR_trace_filter_completer_next,
+ (MR_Completer_Data) data, MR_trace_free_filter_completer_data);
+}
+
+static char *
+MR_trace_filter_completer_next(const char *word, size_t word_len,
+ MR_Completer_Data *completer_data)
+{
+ MR_Filter_Completer_Data *data;
+ char *completion;
+
+ data = (MR_Filter_Completer_Data *) *completer_data;
+ while (1) {
+ completion = MR_trace_completer_list_next(word, word_len,
+ &data->MR_filter_list);
+ if (completion == NULL) {
+ return NULL;
+ } else if (data->MR_filter_func(completion,
+ &(data->MR_filter_data)))
+ {
+ return completion;
+ } else {
+ MR_free(completion);
+ }
+ }
+}
+
+static void
+MR_trace_free_filter_completer_data(MR_Completer_Data completer_data)
+{
+ MR_Filter_Completer_Data *data;
+
+ data = (MR_Filter_Completer_Data *) completer_data;
+ data->MR_filter_free_data(data->MR_filter_data);
+ MR_trace_free_completer_list(data->MR_filter_list);
+ MR_free(data);
+}
+
+MR_Completer_List *
+MR_trace_map_completer(MR_Completer_Map map, MR_Completer_Data map_data,
+ MR_Free_Completer_Data free_data,
+ MR_Completer_List *list)
+{
+ MR_Map_Completer_Data *data;
+
+ data = MR_NEW(MR_Map_Completer_Data);
+ data->MR_map_func = map;
+ data->MR_map_data = map_data;
+ data->MR_map_free_data = free_data;
+ data->MR_map_list = list;
+ return MR_new_completer_elem(MR_trace_map_completer_next,
+ (MR_Completer_Data) data, MR_trace_free_map_completer_data);
+}
+
+static char *
+MR_trace_map_completer_next(const char *word, size_t word_len,
+ MR_Completer_Data *completer_data)
+{
+ MR_Map_Completer_Data *data;
+ char *completion;
+
+ data = (MR_Map_Completer_Data *) *completer_data;
+ completion = MR_trace_completer_list_next(word, word_len,
+ &data->MR_map_list);
+ if (completion == NULL) {
+ return NULL;
+ } else {
+ return data->MR_map_func(completion, &(data->MR_map_data));
+ }
+}
+
+static void
+MR_trace_free_map_completer_data(MR_Completer_Data completer_data)
+{
+ MR_Map_Completer_Data *data;
+
+ data = (MR_Map_Completer_Data *) completer_data;
+ data->MR_map_free_data(data->MR_map_data);
+ MR_trace_free_completer_list(data->MR_map_list);
+ MR_free(data);
+}
+
+MR_Completer_List *
+MR_new_completer_elem(MR_Completer completer, MR_Completer_Data data,
+ MR_Free_Completer_Data free_data)
+{
+ MR_Completer_List *result;
+ result = MR_NEW(MR_Completer_List);
+ result->MR_completer_func = completer;
+ result->MR_completer_func_data = data;
+ result->MR_free_completer_func_data = free_data;
+ result->MR_completer_list_next = NULL;
+ return result;
+}
+
+void
+MR_trace_no_free(MR_Completer_Data data)
+{
+}
+
Index: trace/mercury_trace_completion.h
===================================================================
RCS file: trace/mercury_trace_completion.h
diff -N trace/mercury_trace_completion.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ trace/mercury_trace_completion.h 4 Mar 2002 07:19:44 -0000
@@ -0,0 +1,100 @@
+/*
+** Copyright (C) 2002 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** mercury_trace_completion.h
+**
+** Command line completion for mdb.
+*/
+
+#ifndef MR_TRACE_COMPLETION_H
+#define MR_TRACE_COMPLETION_H
+
+/*
+** Called by Readline when the user requests completion.
+** Examine Readline's input buffer to work out which completers
+** should be used, then apply them.
+** Readline passes zero for `state' on the first call, and non-zero
+** on subsequent calls.
+*/
+extern char *MR_trace_line_completer(const char *word, int state);
+
+/*
+** A MR_Completer is called multiple times with the partial word
+** to complete, the length of the word and some completer-specific data.
+** The completer returns either the next possible completion (which
+** must be allocated with MR_malloc), or NULL if there are no more
+** completions.
+*/
+typedef void *MR_Completer_Data;
+typedef char *(*MR_Completer)(const char *word, size_t word_len,
+ MR_Completer_Data *data);
+
+/* Release the memory held by the completer data. */
+typedef void (*MR_Free_Completer_Data)(MR_Completer_Data data);
+
+typedef struct MR_Completer_List_Struct {
+ MR_Completer MR_completer_func;
+ MR_Completer_Data MR_completer_func_data;
+ MR_Free_Completer_Data MR_free_completer_func_data;
+ struct MR_Completer_List_Struct *MR_completer_list_next;
+} MR_Completer_List;
+
+typedef MR_Completer_List *(*MR_Make_Completer)(const char *word,
+ size_t word_len);
+/* No completions. */
+extern MR_Completer_List *MR_trace_null_completer(const char *word,
+ size_t word_len);
+
+/* Use Readline's filename completer. */
+extern MR_Completer_List *MR_trace_filename_completer(const char *word,
+ size_t word_len);
+
+/*
+** Construct a MR_Completer_List with the given arguments.
+** The MR_completer_list_next field of the structure will be NULL.
+*/
+extern MR_Completer_List *MR_new_completer_elem(MR_Completer completer,
+ MR_Completer_Data data,
+ MR_Free_Completer_Data free_data);
+
+/* Used where the completer data is not malloc'ed. */
+extern void MR_trace_no_free(MR_Completer_Data);
+
+/*
+** Complete on the labels of the elements of a sorted array.
+** A function of type MR_Get_Slot_Name is used to get the label of the
+** element at the given index in the array.
+*/
+typedef char *(*MR_Get_Slot_Name)(int slot);
+extern MR_Completer_List *MR_trace_sorted_array_completer(const char *word,
+ size_t word_length, int array_size,
+ MR_Get_Slot_Name get_slot_name);
+
+/*
+** Apply a filter to the output of a completer.
+** Functions of type MR_Completer_Filter return MR_TRUE if the given
+** string should be included in the list of completions.
+*/
+typedef MR_bool (*MR_Completer_Filter)(const char *completion,
+ MR_Completer_Data *data);
+extern MR_Completer_List *MR_trace_filter_completer(MR_Completer_Filter filter,
+ MR_Completer_Data data,
+ MR_Free_Completer_Data free_data,
+ MR_Completer_List *list);
+
+/*
+** Apply a mapping function to the output of a completer.
+** The MR_Completer_Map function may destructively update its input
+** string, and must MR_free it if it is not returned as the result.
+*/
+typedef char *(*MR_Completer_Map)(char *completion, MR_Completer_Data *data);
+extern MR_Completer_List *MR_trace_map_completer(MR_Completer_Map map_func,
+ MR_Completer_Data data,
+ MR_Free_Completer_Data free_data,
+ MR_Completer_List *list);
+
+#endif MR_TRACE_COMPLETION_H
Index: trace/mercury_trace_help.c
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_help.c,v
retrieving revision 1.17
diff -u -u -r1.17 mercury_trace_help.c
--- trace/mercury_trace_help.c 18 Feb 2002 07:01:30 -0000 1.17
+++ trace/mercury_trace_help.c 3 Mar 2002 17:06:21 -0000
@@ -49,12 +49,22 @@
int slot, const char *text);
static void MR_trace_help_ensure_init(void);
+/* Used for completion of arguments of the `help' command. */
+static char **MR_help_words = NULL;
+static int MR_help_word_max = 0;
+static int MR_help_word_next = 0;
+
+static void MR_trace_add_help_word(const char *word);
+static char * MR_trace_get_help_word(int slot);
+
const char *
MR_trace_add_cat(const char *category, int slot, const char *text)
{
MR_Word path;
MR_trace_help_ensure_init();
+ MR_trace_add_help_word(category);
+
MR_TRACE_USE_HP(
path = MR_list_empty();
);
@@ -70,6 +80,7 @@
const char *result;
MR_trace_help_ensure_init();
+ MR_trace_add_help_word(item);
MR_TRACE_USE_HP(
MR_make_aligned_string_copy(category_on_heap, category);
@@ -197,4 +208,37 @@
MR_trace_help_system_type);
done = MR_TRUE;
}
+}
+
+/*
+** Add the help categories and items to a sorted array for use in completion.
+*/
+static void
+MR_trace_add_help_word(const char *word)
+{
+ MR_bool found;
+ int slot;
+
+ MR_bsearch(MR_help_word_next, slot, found,
+ strcmp(MR_help_words[slot], word));
+ if (!found) {
+ MR_ensure_room_for_next(MR_help_word, char *, 100);
+ MR_prepare_insert_into_sorted(MR_help_words,
+ MR_help_word_next, slot,
+ strcmp(MR_help_words[slot], word));
+ MR_help_words[slot] = MR_copy_string(word);
+ }
+}
+
+MR_Completer_List *
+MR_trace_help_completer(const char *word, size_t word_len)
+{
+ return MR_trace_sorted_array_completer(word, word_len,
+ MR_help_word_next, MR_trace_get_help_word);
+}
+
+static char *
+MR_trace_get_help_word(int slot)
+{
+ return MR_help_words[slot];
}
Index: trace/mercury_trace_help.h
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_help.h,v
retrieving revision 1.1
diff -u -u -r1.1 mercury_trace_help.h
--- trace/mercury_trace_help.h 16 Oct 1998 06:20:17 -0000 1.1
+++ trace/mercury_trace_help.h 3 Mar 2002 17:06:21 -0000
@@ -13,6 +13,8 @@
#ifndef MERCURY_TRACE_HELP_H
#define MERCURY_TRACE_HELP_H
+#include "mercury_trace_completion.h"
+
/*
** These function add a help node, which must a category or an item
** within a category. It returns NULL if the addition was successful,
@@ -39,5 +41,9 @@
extern void MR_trace_help_cat_item(const char *cat,
const char *item);
+
+/* A Readline completer for help topics. */
+extern MR_Completer_List *MR_trace_help_completer(const char *word,
+ size_t word_len);
#endif /* MERCURY_TRACE_HELP_H */
Index: trace/mercury_trace_internal.c
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_internal.c,v
retrieving revision 1.119
diff -u -u -r1.119 mercury_trace_internal.c
--- trace/mercury_trace_internal.c 25 Feb 2002 03:52:52 -0000 1.119
+++ trace/mercury_trace_internal.c 4 Mar 2002 07:25:14 -0000
@@ -215,6 +215,30 @@
MR_MULTIMATCH_ASK, MR_MULTIMATCH_ALL, MR_MULTIMATCH_ONE
} MR_MultiMatch;
+/*
+** A list of the available commands.
+*/
+typedef struct
+{
+ /* The category of command, e.g. "browsing". */
+ const char *MR_trace_command_category;
+
+ /* The command name. */
+ const char *MR_trace_command_name;
+
+ /*
+ ** Some commands take set strings as arguments.
+ ** This field is a NULL terminated array of those strings.
+ */
+ const char *const *MR_trace_command_arg_strings;
+
+ /*
+ ** A function for more arbitrary completion,
+ ** e.g. on predicate names.
+ */
+ const MR_Make_Completer MR_trace_command_arg_completer;
+} MR_Trace_Command_Info;
+
static void MR_trace_internal_ensure_init(void);
static MR_bool MR_trace_internal_create_mdb_window(void);
static void MR_trace_internal_init_from_env(void);
@@ -302,7 +326,12 @@
static void MR_trace_event_print_internal_report(
MR_Event_Info *event_info);
-static MR_bool MR_trace_valid_command(const char *word);
+static const MR_Trace_Command_Info MR_trace_valid_command_list[];
+static const MR_Trace_Command_Info *MR_trace_valid_command(
+ const char *command);
+
+static char *MR_trace_command_completer_next(const char *word,
+ size_t word_len, MR_Completer_Data *data);
MR_Code *
MR_trace_event_internal(MR_Trace_Cmd_Info *cmd, MR_bool interactive,
@@ -4102,100 +4131,189 @@
parent_filename, parent_lineno, indent);
}
-typedef struct
-{
- const char *cat;
- const char *item;
-} MR_trace_cmd_cat_item;
+static const char *const MR_trace_print_cmd_args[] =
+ {"exception", "goal", NULL};
+
+static const char *const MR_trace_set_cmd_args[] =
+ {"format", "depth", "size", "width", "lines",
+ "flat", "pretty", "verbose", NULL};
+
+static const char *const MR_trace_break_cmd_args[] =
+ {"here", "info", NULL};
+
+static const char *const MR_trace_printlevel_cmd_args[] =
+ {"none", "some", "all", NULL};
+
+static const char *const MR_trace_on_off_args[] =
+ {"on", "off", NULL};
+
+static const char *const MR_trace_context_cmd_args[] =
+ {"none", "before", "after", "prevline", "nextline", NULL};
+
+static const char *const MR_trace_scope_cmd_args[] =
+ {"all", "interface", "entry", NULL};
+
+static const char *const MR_trace_table_io_cmd_args[] =
+ {"stats", "start", "end", NULL};
-static MR_trace_cmd_cat_item MR_trace_valid_command_list[] =
+static const MR_Trace_Command_Info MR_trace_valid_command_list[] =
{
/*
- ** The following block is mostly a verbatim copy of the file
- ** doc/mdb_command_list. We do not use a #include to avoid
- ** adding a dependency, and because we want to #ifdef the
- ** experimental commands.
+ ** The first two fields of this block should be the same
+ ** as in the file doc/mdb_command_list.
+ ** XXX Should we allow completion of options of comands?
*/
- { "queries", "query" },
- { "queries", "cc_query" },
- { "queries", "io_query" },
- { "forward", "step" },
- { "forward", "goto" },
- { "forward", "next" },
- { "forward", "finish" },
- { "forward", "exception" },
- { "forward", "return" },
- { "forward", "forward" },
- { "forward", "mindepth" },
- { "forward", "maxdepth" },
- { "forward", "continue" },
- { "backward", "retry" },
- { "browsing", "vars" },
- { "browsing", "print" },
- { "browsing", "browse" },
- { "browsing", "stack" },
- { "browsing", "up" },
- { "browsing", "down" },
- { "browsing", "level" },
- { "browsing", "current" },
- { "browsing", "set" },
- { "browsing", "view" },
- { "breakpoint", "break" },
- { "breakpoint", "ignore" },
- { "breakpoint", "enable" },
- { "breakpoint", "disable" },
- { "breakpoint", "delete" },
- { "breakpoint", "modules" },
- { "breakpoint", "procedures" },
- { "breakpoint", "register" },
- { "parameter", "mmc_options" },
- { "parameter", "printlevel" },
- { "parameter", "echo" },
- { "parameter", "scroll" },
- { "parameter", "context" },
- { "parameter", "scope" },
- { "parameter", "alias" },
- { "parameter", "unalias" },
- { "help", "document_category" },
- { "help", "document" },
- { "help", "help" },
+ { "queries", "query", NULL, MR_trace_null_completer },
+ { "queries", "cc_query", NULL, MR_trace_null_completer },
+ { "queries", "io_query", NULL, MR_trace_null_completer },
+ { "forward", "step", NULL, MR_trace_null_completer },
+ { "forward", "goto", NULL, MR_trace_null_completer },
+ { "forward", "next", NULL, MR_trace_null_completer },
+ { "forward", "finish", NULL, MR_trace_null_completer },
+ { "forward", "exception", NULL, MR_trace_null_completer },
+ { "forward", "return", NULL, MR_trace_null_completer },
+ { "forward", "forward", NULL, MR_trace_null_completer },
+ { "forward", "mindepth", NULL, MR_trace_null_completer },
+ { "forward", "maxdepth", NULL, MR_trace_null_completer },
+ { "forward", "continue", NULL, MR_trace_null_completer },
+ { "backward", "retry", NULL, MR_trace_null_completer },
+ { "browsing", "vars", NULL, MR_trace_null_completer },
+ { "browsing", "print", MR_trace_print_cmd_args,
+ MR_trace_var_completer },
+ { "browsing", "browse", MR_trace_print_cmd_args,
+ MR_trace_var_completer },
+ { "browsing", "stack", NULL, MR_trace_null_completer },
+ { "browsing", "up", NULL, MR_trace_null_completer },
+ { "browsing", "down", NULL, MR_trace_null_completer },
+ { "browsing", "level", NULL, MR_trace_null_completer },
+ { "browsing", "current", NULL, MR_trace_null_completer },
+ { "browsing", "set", MR_trace_set_cmd_args, MR_trace_null_completer },
+ { "browsing", "view", NULL, MR_trace_null_completer },
+ { "breakpoint", "break", MR_trace_break_cmd_args,
+ MR_trace_breakpoint_completer },
+ { "breakpoint", "ignore", NULL, MR_trace_null_completer },
+ { "breakpoint", "enable", NULL, MR_trace_null_completer },
+ { "breakpoint", "disable", NULL, MR_trace_null_completer },
+ { "breakpoint", "delete", NULL, MR_trace_null_completer },
+ { "breakpoint", "modules", NULL, MR_trace_null_completer },
+ { "breakpoint", "procedures", NULL, MR_trace_module_completer },
+ { "breakpoint", "register", NULL, MR_trace_null_completer },
+ { "parameter", "mmc_options", NULL, MR_trace_null_completer },
+ { "parameter", "printlevel", MR_trace_printlevel_cmd_args,
+ MR_trace_null_completer },
+ { "parameter", "echo", MR_trace_on_off_args, MR_trace_null_completer },
+ { "parameter", "scroll", MR_trace_on_off_args,
+ MR_trace_null_completer },
+ { "parameter", "context", MR_trace_context_cmd_args,
+ MR_trace_null_completer },
+ { "parameter", "scope", MR_trace_scope_cmd_args,
+ MR_trace_null_completer },
+ { "parameter", "alias", NULL, MR_trace_command_completer },
+ { "parameter", "unalias", NULL, MR_trace_alias_completer },
+ { "help", "document_category", NULL, MR_trace_null_completer },
+ { "help", "document", NULL, MR_trace_null_completer },
+ { "help", "help", NULL, MR_trace_help_completer },
#ifdef MR_TRACE_HISTOGRAM
- { "exp", "histogram_all" },
- { "exp", "histogram_exp" },
- { "exp", "clear_histogram" },
+ { "exp", "histogram_all", NULL, MR_trace_null_completer },
+ { "exp", "histogram_exp", NULL, MR_trace_null_completer },
+ { "exp", "clear_histogram", NULL, MR_trace_null_completer },
#endif
- { "developer", "nondet_stack" },
+ { "developer", "nondet_stack", NULL, MR_trace_null_completer },
#ifdef MR_USE_MINIMAL_MODEL
- { "developer", "gen_stack" },
+ { "developer", "gen_stack", NULL, MR_trace_null_completer },
#endif
- { "developer", "stack_regs" },
- { "developer", "all_regs" },
- { "developer", "table_io" },
- { "developer", "proc_stats" },
- { "developer", "label_stats" },
- { "developer", "print_optionals" },
- { "misc", "source" },
- { "misc", "save" },
- { "misc", "quit" },
+ { "developer", "stack_regs", NULL, MR_trace_null_completer },
+ { "developer", "all_regs", NULL, MR_trace_null_completer },
+ { "developer", "table_io", MR_trace_table_io_cmd_args,
+ MR_trace_null_completer },
+ { "developer", "proc_stats", NULL, MR_trace_null_completer },
+ { "developer", "label_stats", NULL, MR_trace_null_completer },
+ { "developer", "print_optionals", MR_trace_on_off_args,
+ MR_trace_null_completer },
+ { "misc", "source", NULL, MR_trace_filename_completer },
+ { "misc", "save", NULL, MR_trace_filename_completer },
+ { "misc", "quit", NULL, NULL},
/* End of doc/mdb_command_list. */
- { NULL, "NUMBER" },
- { NULL, "EMPTY" },
- { NULL, NULL },
+ { NULL, "NUMBER", NULL, MR_trace_null_completer },
+ { NULL, "EMPTY", NULL, MR_trace_null_completer },
+ { NULL, NULL, NULL, MR_trace_null_completer },
};
-static MR_bool
+bool
+MR_trace_command_completion_info(const char *word,
+ MR_Make_Completer *completer, const char *const **fixed_args)
+{
+ const MR_Trace_Command_Info *command_info;
+
+ command_info = MR_trace_valid_command(word);
+ if (!command_info) {
+ return FALSE;
+ } else {
+ *completer = command_info->MR_trace_command_arg_completer;
+ *fixed_args = command_info->MR_trace_command_arg_strings;
+ return TRUE;
+ }
+}
+
+static const MR_Trace_Command_Info *
MR_trace_valid_command(const char *word)
{
int i;
- for (i = 0; MR_trace_valid_command_list[i].item != NULL; i++) {
- if (MR_streq(MR_trace_valid_command_list[i].item, word)) {
- return MR_TRUE;
+ for (i = 0;
+ MR_trace_valid_command_list[i].MR_trace_command_name != NULL;
+ i++)
+ {
+ if (MR_streq(
+ MR_trace_valid_command_list[i].MR_trace_command_name,
+ word))
+ {
+ return &MR_trace_valid_command_list[i];
}
}
- return MR_FALSE;
+ return NULL;
+}
+
+MR_Completer_List *
+MR_trace_command_completer(const char *word, size_t word_len)
+{
+ return MR_new_completer_elem(&MR_trace_command_completer_next,
+ (MR_Completer_Data) 0, MR_trace_no_free);
+}
+
+static char *
+MR_trace_command_completer_next(const char *word, size_t word_len,
+ MR_Completer_Data *data)
+{
+ int command_index;
+
+ command_index = (int) *data;
+ while (1) {
+ const char *command;
+ const char *category;
+
+ category = MR_trace_valid_command_list[command_index].
+ MR_trace_command_category;
+ command = MR_trace_valid_command_list[command_index].
+ MR_trace_command_name;
+ command_index++;
+ *data = (void *) command_index;
+
+ /*
+ ** We don't complete on the "EMPTY" and "NUMBER" entries
+ ** in the list of commands (they have a category entry
+ ** of NULL).
+ */
+ if (command == NULL) {
+ return NULL;
+ } else if (category != NULL &&
+ MR_strneq(word, command, word_len))
+ {
+ return MR_copy_string(command);
+ }
+ }
}
void
Index: trace/mercury_trace_internal.h
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_internal.h,v
retrieving revision 1.15
diff -u -u -r1.15 mercury_trace_internal.h
--- trace/mercury_trace_internal.h 18 Feb 2002 07:01:31 -0000 1.15
+++ trace/mercury_trace_internal.h 3 Mar 2002 17:06:21 -0000
@@ -11,6 +11,7 @@
#include "mercury_types.h" /* for MR_Code */
#include "mercury_trace.h" /* for MR_Event_Info, etc. */
#include "mercury_std.h" /* for MR_bool */
+#include "mercury_trace_completion.h" /* for MR_Make_Completer */
#include <stdio.h> /* for FILE */
@@ -73,5 +74,17 @@
FILE *mdb_out);
extern char *MR_trace_get_command(const char *prompt, FILE *mdb_in,
FILE *mdb_out);
+
+/*
+** If word is a valid command, return information about the
+** completer for the command.
+*/
+extern MR_bool MR_trace_command_completion_info(const char *word,
+ MR_Make_Completer *completer,
+ const char *const **fixed_args);
+
+/* A Readline completer for command names. */
+extern MR_Completer_List *MR_trace_command_completer(const char *word,
+ size_t word_len);
#endif /* MERCURY_TRACE_INTERNAL_H */
Index: trace/mercury_trace_readline.c
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_readline.c,v
retrieving revision 1.7
diff -u -u -r1.7 mercury_trace_readline.c
--- trace/mercury_trace_readline.c 13 Feb 2002 09:56:48 -0000 1.7
+++ trace/mercury_trace_readline.c 4 Mar 2002 03:27:33 -0000
@@ -19,13 +19,16 @@
#include "mercury_std.h"
#include "mercury_trace_readline.h"
+#include "mercury_trace_completion.h"
#ifndef MR_NO_USE_READLINE
#ifdef MR_HAVE_READLINE_READLINE_H
#include "readline/readline.h"
#else
- FILE *rl_instream;
- FILE *rl_outstream;
+ extern FILE *rl_instream;
+ extern FILE *rl_outstream;
+ extern char (*rl_completion_entry_function)(const char *, int);
+ extern const char *rl_readline_name;
#endif
#ifdef MR_HAVE_READLINE_HISTORY_H
#include "readline/history.h"
@@ -60,6 +63,16 @@
rl_instream = in;
rl_outstream = out;
+
+ /*
+ ** The cast to (void *) silences a spurious "assignment from
+ ** incompatible pointer type" warning (old versions of
+ ** readline are very sloppy about declaring the types of
+ ** function pointers).
+ */
+ rl_completion_entry_function =
+ (void *) &MR_trace_line_completer;
+ rl_readline_name = "mdb";
line = readline((char *) prompt);
Index: trace/mercury_trace_tables.c
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_tables.c,v
retrieving revision 1.20
diff -u -u -r1.20 mercury_trace_tables.c
--- trace/mercury_trace_tables.c 18 Feb 2002 07:01:31 -0000 1.20
+++ trace/mercury_trace_tables.c 4 Mar 2002 07:27:58 -0000
@@ -17,6 +17,7 @@
#include "mercury_stack_trace.h"
#include "mercury_trace_tables.h"
+#include "mercury_trace_internal.h"
#include "mercury_trace.h"
#include <stdio.h>
@@ -39,7 +40,48 @@
*file_layout, int line,
MR_file_line_callback callback_func, int callback_arg);
-static MR_bool MR_parse_trailing_number(char *start, char **end, int *number);
+static MR_bool MR_parse_trailing_number(char *start, char **end, int *number);
+static char *MR_translate_double_underscores(char *str);
+static char *MR_get_module_info_name(int slot);
+
+typedef struct {
+ MR_PredFunc MR_complete_pf;
+
+ /*
+ ** The word to complete, with `__'
+ ** translated into ':'.
+ */
+ char *MR_complete_name;
+ int MR_complete_name_len;
+ MR_bool MR_complete_name_is_qualified;
+
+ /*
+ ** Slot number of a module for which we should
+ ** return all procedures as completions, -1 if
+ ** there is none.
+ */
+ 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.
+ */
+ int MR_complete_word_matches_module;
+ int MR_complete_current_module;
+ int MR_complete_current_proc;
+} MR_Proc_Completer_Data;
+
+static char *MR_trace_breakpoint_completer_next(const char *word,
+ size_t word_len, MR_Completer_Data *completer_data);
+static void MR_trace_breakpoint_completer_init_module(
+ MR_Proc_Completer_Data *data);
+static char *MR_trace_complete_proc(MR_Proc_Completer_Data *data);
+static char *MR_format_breakpoint_completion(MR_PredFunc pred_or_func,
+ const char *module, const char *name);
+static void MR_free_proc_completer_data(MR_Completer_Data completer_data);
void
MR_register_all_modules_and_procs(FILE *fp, MR_bool verbose)
@@ -224,7 +266,6 @@
char *end;
int n;
int len;
- int double_underscores;
MR_bool found;
spec->MR_proc_module = NULL;
@@ -302,17 +343,8 @@
/*
** Convert all occurences of '__' to ':'.
*/
- double_underscores = 0;
- for (start = str; start < end; start++) {
- if (*start == '_' && *(start + 1) == '_') {
- *(start - double_underscores) = ':';
- double_underscores++;
- start++;
- } else {
- *(start - double_underscores) = *start;
- }
- }
- *(end - double_underscores) = '\0';
+ *end = '\0';
+ MR_translate_double_underscores(str);
spec->MR_proc_module = str;
@@ -327,6 +359,30 @@
return MR_TRUE;
}
+/*
+** Convert all occurrences of `__' to `:'.
+*/
+static char *
+MR_translate_double_underscores(char *start)
+{
+ int double_underscores = 0;
+ char *str;
+
+ str = start;
+ while (*str) {
+ if (*str == '_' && *(str+ 1) == '_') {
+ *(str - double_underscores) = ':';
+ double_underscores++;
+ str++;
+ } else {
+ *(str - double_underscores) = *str;
+ }
+ str++;
+ }
+ *(str - double_underscores) = '\0';
+ return start;
+}
+
/*
** Go backwards over a string starting at `end', stopping at `start',
** parsing the trailing integer and storing it in `*n'.
@@ -472,6 +528,334 @@
f(data, cur_entry);
}
}
+}
+
+MR_Completer_List *
+MR_trace_module_completer(const char *word, size_t word_len)
+{
+ return MR_trace_sorted_array_completer(word, word_len,
+ MR_module_info_next, MR_get_module_info_name);
+}
+
+static char *
+MR_get_module_info_name(int slot)
+{
+ return (char *) MR_module_infos[slot]->MR_ml_name;
+}
+
+MR_Completer_List *
+MR_trace_breakpoint_completer(const char *word, size_t word_len)
+{
+ MR_Proc_Completer_Data *data;
+ int slot;
+ MR_bool found;
+
+ MR_register_all_modules_and_procs(MR_mdb_out, MR_FALSE);
+
+ data = MR_NEW(MR_Proc_Completer_Data);
+
+ if (MR_strneq(word, "pred*", 5)) {
+ data->MR_complete_pf = MR_PREDICATE;
+ word += 5;
+ } else if (MR_strneq(word, "func*", 5)) {
+ data->MR_complete_pf = MR_FUNCTION;
+ word += 5;
+ } else {
+ data->MR_complete_pf = -1;
+ }
+ data->MR_complete_name =
+ MR_translate_double_underscores(MR_copy_string(word));
+ data->MR_complete_name_len = strlen(data->MR_complete_name);
+ data->MR_complete_name_is_qualified =
+ strchr(data->MR_complete_name, ':') != NULL;
+ data->MR_complete_word_matches_module = 0;
+ data->MR_complete_current_module = -1;
+ data->MR_complete_current_proc= -1;
+
+ /*
+ ** 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 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.
+ **
+ ** For example, given word to complete `f' and modules `foo'
+ ** and `bar', we want to return all the procedures in module
+ ** `foo' as completions, 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.
+ */
+ MR_bsearch(MR_module_info_next, slot, found,
+ strncmp(MR_module_infos[slot]->MR_ml_name,
+ data->MR_complete_name, data->MR_complete_name_len));
+ if (found) {
+ data->MR_unambiguous_matching_module = slot;
+ if (slot > 0 &&
+ MR_strneq(MR_module_infos[slot - 1]->MR_ml_name,
+ data->MR_complete_name,
+ data->MR_complete_name_len))
+ {
+ data->MR_unambiguous_matching_module = -1;
+ }
+ if (slot < MR_module_info_next - 1 &&
+ MR_strneq(MR_module_infos[slot + 1]->MR_ml_name,
+ data->MR_complete_name,
+ data->MR_complete_name_len))
+ {
+ data->MR_unambiguous_matching_module = -1;
+ }
+ } else {
+ data->MR_unambiguous_matching_module = -1;
+ }
+
+ return MR_new_completer_elem(MR_trace_breakpoint_completer_next,
+ (MR_Completer_Data) data,
+ MR_free_proc_completer_data);
+}
+
+static char *
+MR_trace_breakpoint_completer_next(const char *dont_use_this_word,
+ size_t dont_use_this_len, MR_Completer_Data *completer_data)
+{
+ MR_Proc_Completer_Data *data;
+ char *name;
+ size_t name_len;
+ const char *module_name;
+ int module_name_len;
+ char *completion;
+
+ data = (MR_Proc_Completer_Data *) *completer_data;
+
+ name = data->MR_complete_name;
+ name_len = data->MR_complete_name_len;
+
+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)
+ {
+ /*
+ ** Move on to the next module.
+ */
+ data->MR_complete_current_module++;
+ if (data->MR_complete_current_module >= MR_module_info_next) {
+ return NULL;
+ }
+ MR_trace_breakpoint_completer_init_module(data);
+
+ /*
+ ** Complete on the module name if we aren't finding
+ ** qualified completions in this module.
+ */
+ module_name = MR_module_infos[data->MR_complete_current_module]
+ ->MR_ml_name;
+ if (data->MR_complete_word_matches_module == 0 &&
+ MR_strneq(name, module_name, name_len))
+ {
+ return MR_format_breakpoint_completion(
+ data->MR_complete_pf, module_name, "");
+ } else {
+ goto try_completion;
+ }
+ } else {
+ /*
+ ** Complete on the next procedure in the current module.
+ */
+ completion = MR_trace_complete_proc(data);
+
+ if (completion != NULL) {
+ return completion;
+ } else {
+ goto try_completion;
+ }
+ }
+}
+
+/*
+** Set up the completer data for processing a module.
+*/
+static void
+MR_trace_breakpoint_completer_init_module(MR_Proc_Completer_Data *data)
+{
+ char *name;
+ size_t name_len;
+ char *module_name;
+ int module_name_len;
+
+ name = data->MR_complete_name;
+ name_len = data->MR_complete_name_len;
+
+ module_name = (char *)
+ MR_module_infos[data->MR_complete_current_module]->MR_ml_name;
+ module_name_len = strlen(module_name);
+
+ /*
+ ** Work out whether we should find qualified completions
+ ** for procedures in this module.
+ */
+ if (MR_strneq(module_name, name, module_name_len)
+ && name_len > module_name_len
+ && name[module_name_len] == ':'
+ && strchr(name + module_name_len + 1, ':') == NULL)
+ {
+ /*
+ ** The name to complete matches the module name completely.
+ ** When searching for qualified completions skip past
+ ** the module name and the trailing ':'.
+ */
+ data->MR_complete_word_matches_module = module_name_len + 1;
+ } else if (data->MR_complete_current_module ==
+ data->MR_unambiguous_matching_module)
+ {
+ /*
+ ** The name to complete matches the module name partially,
+ ** and does not match any other module name. We will be
+ ** matching all procedures, use the empty string as the
+ ** name to match against.
+ */
+ data->MR_complete_word_matches_module = name_len;
+ } else {
+ data->MR_complete_word_matches_module = 0;
+ }
+
+ /*
+ ** If the name to complete is qualified, we should only
+ ** complete on procedures if the module name matches.
+ */
+ if (data->MR_complete_name_is_qualified &&
+ data->MR_complete_word_matches_module == 0)
+ {
+ data->MR_complete_current_proc = -1;
+ } else {
+ data->MR_complete_current_proc = 0;
+ }
+}
+
+/*
+** Check whether the current procedure matches the word to be completed.
+** To do: complete on arity and mode number.
+*/
+static char *
+MR_trace_complete_proc(MR_Proc_Completer_Data *data)
+{
+ char *completion;
+ char *name;
+ int name_len;
+ char *unqualified_name;
+ int unqualified_name_len;
+ char *complete_module;
+ const MR_Module_Layout *module_layout;
+ const MR_Proc_Layout *proc_layout;
+
+ name = data->MR_complete_name;
+ name_len = data->MR_complete_name_len;
+
+ unqualified_name = name + data->MR_complete_word_matches_module;
+ unqualified_name_len =
+ name_len - data->MR_complete_word_matches_module;
+
+ module_layout = MR_module_infos[data->MR_complete_current_module];
+ proc_layout =
+ module_layout->MR_ml_procs[data->MR_complete_current_proc];
+
+ if (
+ ! MR_PROC_LAYOUT_COMPILER_GENERATED(proc_layout) &&
+ ( data->MR_complete_pf == -1 ||
+ proc_layout->MR_sle_user.MR_user_pred_or_func ==
+ data->MR_complete_pf
+ ) &&
+ MR_strneq(proc_layout->MR_sle_user.MR_user_name,
+ unqualified_name, unqualified_name_len))
+ {
+ if (data->MR_complete_word_matches_module != 0) {
+ complete_module = (char *) module_layout->MR_ml_name;
+ } else {
+ complete_module = NULL;
+ }
+ completion = MR_format_breakpoint_completion(
+ data->MR_complete_pf, complete_module,
+ proc_layout->MR_sle_user.MR_user_name);
+ } else {
+ completion = NULL;
+ }
+
+ /*
+ ** Move on to the next procedure in the current module.
+ */
+ data->MR_complete_current_proc++;
+
+ if (data->MR_complete_word_matches_module != 0
+ && data->MR_complete_current_proc >=
+ module_layout->MR_ml_proc_count
+ && ! data->MR_complete_name_is_qualified)
+ {
+ /*
+ ** We've finished checking for module qualified completions
+ ** in this module, now check for unqualified completions
+ ** if the word to complete doesn't contain a qualifier.
+ */
+ data->MR_complete_word_matches_module = 0;
+ data->MR_complete_current_proc = 0;
+ }
+
+ return completion;
+}
+
+static char *
+MR_format_breakpoint_completion(MR_PredFunc pred_or_func,
+ const char *module, const char *name)
+{
+ int size;
+ int module_len;
+ int offset;
+ char *completion;
+
+ size = strlen(name);
+ if (pred_or_func != -1) {
+ size += 5;
+ }
+ if (module != NULL) {
+ /* +1 for the ':' */
+ module_len = strlen(module);
+ size += module_len + 1;
+ }
+ completion = MR_malloc(size + 1);
+
+ offset = 0;
+ if (pred_or_func == MR_PREDICATE) {
+ strcpy(completion, "pred*");
+ offset += 5;
+ } else if (pred_or_func == MR_FUNCTION) {
+ strcpy(completion, "func*");
+ offset += 5;
+ }
+
+ if (module != NULL) {
+ strcpy(completion + offset, module);
+ offset += module_len;
+ completion[offset] = ':';
+ offset++;
+ }
+
+ strcpy(completion + offset, name);
+
+ return completion;
+}
+
+static void
+MR_free_proc_completer_data(MR_Completer_Data completer_data)
+{
+ MR_Proc_Completer_Data *data;
+
+ data = (MR_Proc_Completer_Data *) completer_data;
+
+ MR_free(data->MR_complete_name);
+ MR_free(data);
}
void
Index: trace/mercury_trace_tables.h
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_tables.h,v
retrieving revision 1.11
diff -u -u -r1.11 mercury_trace_tables.h
--- trace/mercury_trace_tables.h 18 Feb 2002 07:01:32 -0000 1.11
+++ trace/mercury_trace_tables.h 3 Mar 2002 17:44:56 -0000
@@ -15,6 +15,7 @@
#define MERCURY_TRACE_TABLES_H
#include "mercury_stack_layout.h"
+#include "mercury_trace_completion.h"
#include <stdio.h>
/*
@@ -158,5 +159,11 @@
*/
extern void MR_label_layout_stats(FILE *fp);
+
+/* A Readline completer for module names. */
+extern MR_Completer_List *MR_trace_module_completer(const char *, size_t);
+
+/* A Readline completer for breakpoint specifications. */
+extern MR_Completer_List *MR_trace_breakpoint_completer(const char *, size_t);
#endif /* not MERCURY_TRACE_TABLES_H */
Index: trace/mercury_trace_vars.c
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_vars.c,v
retrieving revision 1.37
diff -u -u -r1.37 mercury_trace_vars.c
--- trace/mercury_trace_vars.c 24 Feb 2002 11:53:46 -0000 1.37
+++ trace/mercury_trace_vars.c 3 Mar 2002 17:06:21 -0000
@@ -125,6 +125,8 @@
char *path, MR_Browser browser,
MR_Browse_Caller_Type caller,
MR_Browse_Format format);
+static char * MR_trace_var_completer_next(const char *word,
+ size_t word_len, MR_Completer_Data *data);
static const char * MR_trace_bad_path(const char *path);
static int MR_trace_print_var_name(FILE *out, MR_Var_Details *var);
static const char * MR_trace_valid_var_number(int var_number);
@@ -1150,6 +1152,32 @@
}
(*browser)((MR_Word) typeinfo, *value, caller, format);
+ return NULL;
+}
+
+MR_Completer_List *
+MR_trace_var_completer(const char *word, size_t word_len)
+{
+ return MR_new_completer_elem(&MR_trace_var_completer_next,
+ (MR_Completer_Data) 0, MR_trace_no_free);
+}
+
+static char *
+MR_trace_var_completer_next(const char *word, size_t word_len,
+ MR_Completer_Data *data)
+{
+ int slot;
+ const char *var_name;
+
+ slot = (int) *data;
+ while (slot < MR_point.MR_point_var_count) {
+ var_name = MR_point.MR_point_vars[slot].MR_var_fullname;
+ slot++;
+ if (MR_strneq(var_name, word, word_len)) {
+ *data = (MR_Completer_Data) slot;
+ return MR_copy_string(var_name);
+ }
+ }
return NULL;
}
Index: trace/mercury_trace_vars.h
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_vars.h,v
retrieving revision 1.17
diff -u -u -r1.17 mercury_trace_vars.h
--- trace/mercury_trace_vars.h 24 Feb 2002 11:53:46 -0000 1.17
+++ trace/mercury_trace_vars.h 3 Mar 2002 17:06:21 -0000
@@ -49,6 +49,7 @@
#include "mercury_type_info.h" /* for MR_TypeInfo */
#include "mercury_trace_base.h" /* for MR_Trace_Port */
#include "mercury_trace_browse.h" /* for MR_Browser */
+#include "mercury_trace_completion.h" /* for MR_Completer_List */
typedef void (*MR_Browser)(MR_Word type_info, MR_Word value,
MR_Browse_Caller_Type caller, MR_Browse_Format format);
@@ -191,5 +192,9 @@
const MR_Label_Layout *level_layout,
MR_Word *base_sp, MR_Word *base_curfr,
int ancestor_level, MR_bool print_optionals);
+
+/* A Readline completer for variable names. */
+extern MR_Completer_List *MR_trace_var_completer(const char *word,
+ size_t word_len);
#endif /* MERCURY_TRACE_VARS_H */
--------------------------------------------------------------------------
mercury-reviews mailing list
post: mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------
More information about the reviews
mailing list