[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