Add support for command line completion to mdb.

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

runtime/mercury_wrapper.{c,h}
	Add a runtime option `--force-readline', which tells mdb to
	use readline even if MR_mdb_in is not a tty. This is needed
	for tests/debugger/completion. `--force-readline' is not
	documented because I'm not sure that it will work properly
	in all situations (it's fine for the test).

	Fix capitalisation in references to the Mercury User's Guide
	in error messages.

trace/mercury_trace_readline.c:
	Tell Readline to use our completer.

	Handle `--force-readline'. Disable some Readline terminal
	initialisation code which reports spurious warnings if the
	input stream is not a tty.

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 arbitrary 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().

tests/debugger/Mmakefile:
tests/debugger/completion.m:
tests/debugger/completion.exp:
tests/debugger/completion.inp:
tests/debugger/completion.inputrc:
tests/debugger/completion.sub1.m:
tests/debugger/completion.sub2.m:
tests/debugger/completion.sub2.sub3.m:
	Test case.
This commit is contained in:
Simon Taylor
2002-03-06 14:35:06 +00:00
parent d62c697231
commit 32051f5467
28 changed files with 1764 additions and 112 deletions

3
NEWS
View File

@@ -202,6 +202,9 @@ Changes to the Mercury implementation:
* 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

View File

@@ -1,5 +1,5 @@
/*
** Copyright (C) 1998-2000 The University of Melbourne.
** Copyright (C) 1998-2000,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.
*/
@@ -123,7 +123,7 @@
} while(0)
/*
** MR_bsearch(int next, int& element, MR_bool& found, COMPARE)
** MR_bsearch(int num_elements, int& element, MR_bool& found, COMPARE)
**
** Given a sorted array, this macro performs a binary search.
** If the search is successful, MR_bsearch sets the `found' parameter
@@ -131,7 +131,8 @@
** If the search is unsuccessful, MR_bsearch sets `found' to MR_FALSE;
** `element' will be clobbered.
**
** The number of the elements in the array is given by the `next' parameter.
** The number of the elements in the array is given by the `num_elements'
** parameter.
** The `COMPARE' parameter should be an expression of type int which compares
** the value at the index specified by the current value of `element'
** with the desired value, and returns <0, 0, or >0 according to whether
@@ -142,7 +143,7 @@
** parameter.
*/
#define MR_bsearch(next, element, found, COMPARE) \
#define MR_bsearch(num_elements, element, found, COMPARE) \
do { \
int lo; \
int hi; \
@@ -155,7 +156,7 @@
*/ \
(element) = 0; \
lo = 0; \
hi = (next) - 1; \
hi = (num_elements) - 1; \
(found) = MR_FALSE; \
while (lo <= hi) { \
(element) = (lo + hi) / 2; \
@@ -171,6 +172,28 @@
} \
} while(0)
/*
** MR_find_first_match(int num_elements, 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(num_elements, element, found, COMPARE) \
do { \
MR_bsearch((num_elements), (element), (found), (COMPARE)); \
if (found) { \
while ((element) > 0) { \
(element)--; \
if ((COMPARE) != 0) { \
(element)++; \
break; \
} \
} \
} \
} while (0)
/*
** MR_prepare_insert_into_sorted(array[], int& next, int& element, COMPARE)
**

View File

@@ -269,10 +269,14 @@ MR_copy_string(const char *s)
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;
}
}
/*---------------------------------------------------------------------------*/

View File

@@ -1,5 +1,5 @@
/*
** Copyright (C) 1994-2000 The University of Melbourne.
** Copyright (C) 1994-2000,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.
*/
@@ -80,6 +80,7 @@ extern void *MR_malloc(size_t n);
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)))

View File

@@ -103,6 +103,9 @@ const char *MR_mdb_out_filename = NULL;
const char *MR_mdb_err_filename = NULL;
MR_bool MR_mdb_in_window = MR_FALSE;
/* use readline() in the debugger even if the input stream is not a tty */
MR_bool MR_force_readline = MR_FALSE;
/* other options */
MR_bool MR_check_space = MR_FALSE;
@@ -753,6 +756,7 @@ enum MR_long_option {
MR_MDB_OUT,
MR_MDB_ERR,
MR_MDB_IN_WINDOW,
MR_FORCE_READLINE,
MR_NUM_OUTPUT_ARGS
};
@@ -773,6 +777,7 @@ struct MR_option MR_long_opts[] = {
{ "mdb-out", 1, 0, MR_MDB_OUT },
{ "mdb-err", 1, 0, MR_MDB_ERR },
{ "mdb-in-window", 0, 0, MR_MDB_IN_WINDOW },
{ "force-readline", 0, 0, MR_FORCE_READLINE },
{ "num-output-args", 1, 0, MR_NUM_OUTPUT_ARGS }
};
@@ -901,6 +906,18 @@ process_options(int argc, char **argv)
MR_mdb_in_window = MR_TRUE;
break;
case MR_FORCE_READLINE:
MR_force_readline = MR_TRUE;
#ifdef MR_NO_USE_READLINE
printf(
"Mercury runtime: `--force-readline' is specified in MERCURY_OPTIONS\n");
printf(
"but readline() is not available.\n");
fflush(stdout);
exit(1);
#endif
break;
case 'a':
benchmark_all_solns = MR_TRUE;
break;
@@ -1092,7 +1109,7 @@ process_options(int argc, char **argv)
"the word `%s'\n"
"which is not an option. Please refer to the "
"Environment Variables section\n"
"of the Mercury user's guide for details.\n",
"of the Mercury User's Guide for details.\n",
argv[MR_optind]);
fflush(stdout);
exit(1);
@@ -1105,7 +1122,7 @@ usage(void)
printf("The MERCURY_OPTIONS environment variable "
"contains an invalid option.\n"
"Please refer to the Environment Variables section of "
"the Mercury\nuser's guide for details.\n");
"the Mercury\nUser's Guide for details.\n");
fflush(stdout);
exit(1);
} /* end usage() */

View File

@@ -220,6 +220,9 @@ extern const char *MR_mdb_err_filename;
/* should mdb be started in a window */
extern MR_bool MR_mdb_in_window;
/* use readline() in the debugger even if the input stream is not a tty */
extern MR_bool MR_force_readline;
/* size of the primary cache */
extern size_t MR_pcache_size;

View File

@@ -28,6 +28,7 @@ NONRETRY_PROGS = \
breakpoints \
browse_pretty \
cmd_quote \
completion \
debugger_regs \
exception_cmd \
exception_value \
@@ -46,6 +47,10 @@ NONRETRY_PROGS = \
resume_typeinfos \
shallow
# The completion test requires mdb to use readline, even though
# the input is not a terminal.
MLFLAGS-completion = --runtime-flags --force-readline
MCFLAGS-shallow = --trace shallow
MCFLAGS-tabled_read = --trace-table-io
MCFLAGS-tabled_read_decl = --trace-table-io-decl
@@ -129,6 +134,11 @@ cmd_quote.out: cmd_quote cmd_quote.inp
$(MDB) ./cmd_quote < cmd_quote.inp 2>&1 | \
sed 's/io.m:[0-9]*/io.m:NNNN/g' > cmd_quote.out 2>&1
# Set up readline to make it easier to use completion non-interactively.
completion.out: completion completion.inp
INPUTRC=completion.inputrc $(MDB) ./completion \
< completion.inp > completion.out 2>&1
debugger_regs.out: debugger_regs debugger_regs.inp
$(MDB) ./debugger_regs < debugger_regs.inp > debugger_regs.out 2>&1

View File

@@ -0,0 +1,96 @@
Melbourne Mercury Debugger, mdb version DEV.
Copyright 1998 The University of Melbourne, Australia.
mdb is free software, covered by the GNU General Public License.
There is absolutely no warranty for mdb.
1: 1 1 CALL pred completion:main/2-0 (det) completion.m:13
mdb> echo on
Command echo enabled.
mdb>
? down maxdepth return
P e mindepth s
alias echo mmc_options save
all_regs enable modules scope
b exception next scroll
break excp nondet_stack set
browse f p source
c finish print stack
cc_query forward print_optionals stack_regs
context g printlevel step
continue goto proc_stats table_io
current h procedures unalias
d help query up
delete ignore quit v
disable io_query r vars
document label_stats register view
document_category level retry
h help
vars view
help vars
vars
Prints the names of all the known variables in the current
environment, together with an ordinal number for each variable.
mdb>
* --pretty -f -v exception
--flat --verbose -p HeadVar__1 goal
p --flat HeadVar__1
HeadVar__1 state('<<c_pointer>>')
mdb>
stack stack_regs
stack --detailed
0 1 1 1 pred completion:main/2-0 (det) (completion.m:13) (empty)
mdb>
proc_stats procedures
completion completion:sub2
completion:sub1 completion:sub2:sub3
completion:sub1 completion:sub2 completion:sub2:sub3
procedures completion:sub1
Registering debuggable procedures... done.
There are 4 debuggable modules, with a total of 7 procedures.
List of procedures in module `completion:sub1'
pred completion:sub1:zp/1-0 (det)
func completion:sub1:z1/0-0 (det)
mdb> set --flat format pretty
mdb> unalias excp
Alias `excp' removed.
mdb>
z z1 z2 zabc3 zp zz
b zabc3
0: + stop interface func completion:sub2:sub3:zabc3/0-0 (det)
mdb>
pred*completion: pred*completion:sub2:sub3:
pred*completion:sub1: pred*main
pred*completion:sub2: pred*zp
b pred*zp
1: + stop interface pred completion:sub1:zp/1-0 (det)
mdb>
completion: completion:sub2:
completion:sub1: completion:sub2:sub3:
completion:sub1: completion:sub2: completion:sub2:sub3:
completion:sub1:z1 completion:sub1:zp
b completion:sub1:z1
2: + stop interface func completion:sub1:z1/0-0 (det)
mdb>
completion: completion:sub2:
completion:sub1: completion:sub2:sub3:
completion:sub1: completion:sub2: completion:sub2:sub3:
completion:sub2: completion:sub2:sub3:
b completion:sub2:sub3:zabc3
3: + stop interface func completion:sub2:sub3:zabc3/0-0 (det)
mdb>
2d 2disable 2document_category
2delete 2document 2down
2delete
2: E stop interface func completion:sub1:z1/0-0 (det)
mdb> c
ok

View File

@@ -0,0 +1,13 @@
echo on
@h@e@v@a@
p --f@@H@
sta@ @
proc@e@compl@:@1
set --f@fo@p@
un@ex@
b z@a@
b pred*@z@
b compl@s@1@1
b compl@s@2@s@
2d@e@
c

View File

@@ -0,0 +1,9 @@
# Don't query the user if there are too many completions.
set completion-query-items 100000
# Display all completions immediately, without
# requiring multiple `complete' commands.
set show-all-if-ambiguous on
# Make the completion requests show up in the input file.
@: complete

View File

@@ -0,0 +1,20 @@
:- module completion.
:- interface.
:- import_module io.
:- include_module completion__sub1, completion__sub2.
:- pred main(io__state::di, io__state::uo) is det.
:- implementation.
main -->
io__write_string("ok\n").
:- func z = int.
z = 0.
:- func zz = int.
zz = 0.

View File

@@ -0,0 +1,13 @@
:- module completion__sub1.
:- interface.
:- func z1 = int.
:- pred zp(int::out) is det.
:- implementation.
z1 = 1.
zp(1).

View File

@@ -0,0 +1,11 @@
:- module completion__sub2.
:- interface.
:- include_module completion__sub2__sub3.
:- func z2 = int.
:- implementation.
z2 = 2.

View File

@@ -0,0 +1,9 @@
:- module completion__sub2__sub3.
:- interface.
:- func zabc3 = int.
:- implementation.
zabc3 = 3.

View File

@@ -41,6 +41,7 @@ HDRS = \
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 @@ CFILES = \
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 \

View File

@@ -1,5 +1,5 @@
/*
** Copyright (C) 1998-2000 The University of Melbourne.
** Copyright (C) 1998-2000,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.
*/
@@ -25,6 +25,9 @@ static int MR_alias_record_next = 0;
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 @@ MR_trace_print_alias_num(FILE *fp, int slot, MR_bool mdb_command_format)
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"));
}

View File

@@ -1,5 +1,5 @@
/*
** Copyright (C) 1998,2000-2001 The University of Melbourne.
** Copyright (C) 1998,2000-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.
*/
@@ -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_alias(FILE *fp, const char *name);
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 */

View File

@@ -0,0 +1,574 @@
/*
** 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
/*
** 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_prepend_string(char *completion, MR_Completer_Data *data);
/*---------------------------------------------------------------------------*/
/*
** 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.
*/
char *MR_trace_line_completer(const char *passed_word, int state)
{
#ifdef MR_NO_USE_READLINE
return NULL;
#else
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.
*/
#define MR_MAX_COMMAND_NAME_LEN 256
char command[MR_MAX_COMMAND_NAME_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_NAME_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;
#endif /* ! MR_NO_USE_READLINE */
}
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);
}
}
/*---------------------------------------------------------------------------*/
/* No completions. */
MR_Completer_List *
MR_trace_null_completer(const char *word, size_t word_len)
{
return NULL;
}
/*---------------------------------------------------------------------------*/
/* Complete on the labels of a sorted array. */
typedef struct {
MR_Get_Slot_Name MR_sorted_array_get_slot_name;
int MR_sorted_array_current_offset;
int MR_sorted_array_size;
} MR_Sorted_Array_Completer_Data;
static char *MR_trace_sorted_array_completer_next(const char *word,
size_t word_length, MR_Completer_Data *data);
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_current_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_current_offset
< data->MR_sorted_array_size)
{
completion = data->MR_sorted_array_get_slot_name(
data->MR_sorted_array_current_offset);
if (MR_strneq(completion, word, word_length)) {
data->MR_sorted_array_current_offset++;
return MR_copy_string(completion);
} else {
return NULL;
}
} else {
return NULL;
}
}
/*---------------------------------------------------------------------------*/
/* Complete on the elements of an unsorted array of strings. */
typedef struct MR_String_Array_Completer_Data_struct {
char **MR_string_array;
int MR_string_array_current_offset;
} MR_String_Array_Completer_Data;
static char *MR_trace_string_array_completer_next(const char *word,
size_t word_len, MR_Completer_Data *data);
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->MR_string_array = (char **) strings;
data->MR_string_array_current_offset = 0;
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->MR_string_array[
completer_data->MR_string_array_current_offset];
completer_data->MR_string_array_current_offset++;
if (result == NULL) {
return NULL;
} else {
if (strncmp(result, word, word_len) == 0) {
return MR_copy_string(result);
}
}
}
}
/*---------------------------------------------------------------------------*/
/* Use Readline's filename completer. */
static char *MR_trace_filename_completer_next(const char *word,
size_t word_len, MR_Completer_Data *);
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);
}
/*---------------------------------------------------------------------------*/
/* Apply a filter to the output of a completer. */
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;
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);
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);
}
/*---------------------------------------------------------------------------*/
/* Apply a mapping function to the output of a completer. */
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;
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);
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)
{
}

View File

@@ -0,0 +1,103 @@
/*
** 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
#include "mercury_std.h" /* for MR_bool */
#include <stdlib.h> /* for size_t */
/*
** 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

View File

@@ -49,12 +49,22 @@ static const char *MR_trace_help_add_node(MR_Word path, const char *name,
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 @@ MR_trace_add_item(const char *category, const char *item, int slot,
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);
@@ -198,3 +209,36 @@ MR_trace_help_ensure_init(void)
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];
}

View File

@@ -1,5 +1,5 @@
/*
** Copyright (C) 1998 The University of Melbourne.
** Copyright (C) 1998,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.
*/
@@ -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,
@@ -40,4 +42,8 @@ extern void MR_trace_help_word(const char *word);
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 */

View File

@@ -215,6 +215,31 @@ typedef enum {
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 fixed strings as arguments.
** This field is a NULL terminated array of those strings,
** or NULL if there are no fixed 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 +327,12 @@ static void MR_insert_line_at_tail(const char *line);
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 +4132,247 @@ MR_trace_event_print_internal_report(MR_Event_Info *event_info)
parent_filename, parent_lineno, indent);
}
typedef struct
{
const char *cat;
const char *item;
} MR_trace_cmd_cat_item;
static const char *const MR_trace_movement_cmd_args[] =
{"-N", "-S", "-a", "-n", "-s",
"--none", "--some", "--all", "--strict", "--no-strict", NULL};
static MR_trace_cmd_cat_item MR_trace_valid_command_list[] =
static const char *const MR_trace_print_cmd_args[] =
{"-f", "-p", "-v", "--flat", "--pretty", "--verbose",
"exception", "goal", "*", NULL};
/*
** It's better to have a single completion where possible,
** so don't include `-d' here.
*/
static const char *const MR_trace_stack_cmd_args[] =
{"--detailed", NULL};
static const char *const MR_trace_set_cmd_args[] =
{"-A", "-B", "-P", "-f", "-p", "-v",
"--print-all", "--print", "--browse",
"--flat", "--pretty", "--verbose",
"format", "depth", "size", "width", "lines",
"flat", "pretty", "verbose", NULL};
static const char *const MR_trace_view_cmd_args[] =
{"-c", "-f", "-n", "-s", "-t", "-v", "-w", "-2",
"--close", "--verbose", "--force", "--split-screen",
"--window-command", "--server-command", "--server-name",
"--timeout", NULL};
static const char *const MR_trace_break_cmd_args[] =
{"-A", "-E", "-I", "-O", "-P", "-S", "-a", "-e", "-i",
"--all", "--entry", "--ignore-entry", "--ignore-interface",
"--interface", "--print", "--select-all", "--select-one",
"--stop", "here", "info", NULL};
static const char *const MR_trace_ignore_cmd_args[] =
{"-E", "-I", "--ignore-entry", "--ignore-interface", 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};
/*
** It's better to have a single completion where possible,
** so don't include `-i' here.
*/
static const char *const MR_trace_source_cmd_args[] =
{"--ignore-errors", NULL};
static const char *const MR_trace_quit_cmd_args[] =
{"-y", NULL};
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.
*/
{ "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" },
/*
** XXX For queries we should complete on all modules, not
** just those that were compiled with tracing enabled.
*/
{ "queries", "query", NULL, MR_trace_module_completer },
{ "queries", "cc_query", NULL, MR_trace_module_completer },
{ "queries", "io_query", NULL, MR_trace_module_completer },
{ "forward", "step", MR_trace_movement_cmd_args,
MR_trace_null_completer },
{ "forward", "goto", MR_trace_movement_cmd_args,
MR_trace_null_completer },
{ "forward", "next", MR_trace_movement_cmd_args,
MR_trace_null_completer },
{ "forward", "finish", MR_trace_movement_cmd_args,
MR_trace_null_completer },
{ "forward", "exception", MR_trace_movement_cmd_args,
MR_trace_null_completer },
{ "forward", "return", MR_trace_movement_cmd_args,
MR_trace_null_completer },
{ "forward", "forward", MR_trace_movement_cmd_args,
MR_trace_null_completer },
{ "forward", "mindepth", MR_trace_movement_cmd_args,
MR_trace_null_completer },
{ "forward", "maxdepth", MR_trace_movement_cmd_args,
MR_trace_null_completer },
{ "forward", "continue", MR_trace_movement_cmd_args,
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", MR_trace_stack_cmd_args,
MR_trace_null_completer },
{ "browsing", "up", MR_trace_stack_cmd_args,
MR_trace_null_completer },
{ "browsing", "down", MR_trace_stack_cmd_args,
MR_trace_null_completer },
{ "browsing", "level", MR_trace_stack_cmd_args,
MR_trace_null_completer },
{ "browsing", "current", NULL, MR_trace_null_completer },
{ "browsing", "set", MR_trace_set_cmd_args, MR_trace_null_completer },
{ "browsing", "view", MR_trace_view_cmd_args,
MR_trace_null_completer },
{ "breakpoint", "break", MR_trace_break_cmd_args,
MR_trace_breakpoint_completer },
{ "breakpoint", "ignore", MR_trace_ignore_cmd_args,
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_filename_completer },
{ "exp", "histogram_exp", NULL, MR_trace_filename_completer },
{ "exp", "clear_histogram", NULL, MR_trace_null_completer },
#endif
{ "developer", "nondet_stack" },
{ "developer", "nondet_stack", MR_trace_stack_cmd_args,
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_filename_completer },
{ "developer", "label_stats", NULL, MR_trace_filename_completer },
{ "developer", "print_optionals", MR_trace_on_off_args,
MR_trace_null_completer },
{ "misc", "source", MR_trace_source_cmd_args,
MR_trace_filename_completer },
{ "misc", "save", NULL, MR_trace_filename_completer },
{ "misc", "quit", MR_trace_quit_cmd_args, 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

View File

@@ -1,5 +1,5 @@
/*
** Copyright (C) 1998-2001 The University of Melbourne.
** Copyright (C) 1998-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.
*/
@@ -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 */
@@ -74,4 +75,16 @@ extern char *MR_trace_getline(const char *prompt, FILE *mdb_in,
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 */

View File

@@ -1,5 +1,5 @@
/*
** Copyright (C) 1998-2001 The University of Melbourne.
** Copyright (C) 1998-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.
*/
@@ -17,15 +17,21 @@
#include "mercury_array_macros.h"
#include "mercury_memory.h"
#include "mercury_std.h"
#include "mercury_wrapper.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;
extern void (*rl_prep_term_function)(int);
extern void (*rl_deprep_term_function)(void);
#endif
#ifdef MR_HAVE_READLINE_HISTORY_H
#include "readline/history.h"
@@ -41,6 +47,9 @@
/* The initial size of the array of characters used to hold the line. */
#define MR_INIT_BUF_LEN 80
static void MR_dummy_prep_term_function(int ignored);
static void MR_dummy_deprep_term_function(void);
/*
** Print the prompt to the `out' file, read a line from the `in' file,
** and return it in a MR_malloc'd buffer holding the line (without the
@@ -54,13 +63,39 @@ MR_trace_readline(const char *prompt, FILE *in, FILE *out)
#if (defined(isatty) || defined(MR_HAVE_ISATTY)) \
&& (defined(fileno) || defined(MR_HAVE_FILENO)) \
&& !defined(MR_NO_USE_READLINE)
/* use readline, if the input file is a terminal */
if (isatty(fileno(in))) {
char *line;
char *line;
MR_bool in_isatty;
in_isatty = isatty(fileno(in));
if (in_isatty || MR_force_readline) {
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";
if (!in_isatty) {
/*
** This is necessary for tests/debugger/completion,
** otherwise we get lots of messages about readline
** not being able to get the terminal settings.
** This is possibly a bit flaky, but it's only
** used by our tests.
*/
rl_prep_term_function =
(void *) MR_dummy_prep_term_function;
rl_deprep_term_function =
(void *) MR_dummy_deprep_term_function;
}
line = readline((char *) prompt);
/*
@@ -124,3 +159,13 @@ MR_trace_readline_raw(FILE *fp)
return NULL;
}
}
static void
MR_dummy_prep_term_function(int ignored)
{
}
static void
MR_dummy_deprep_term_function(void)
{
}

View File

@@ -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 @@ static void MR_process_line_layouts(const MR_Module_File_Layout
*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 void 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 @@ MR_parse_proc_spec(char *str, MR_Proc_Spec *spec)
char *end;
int n;
int len;
int double_underscores;
MR_bool found;
spec->MR_proc_module = NULL;
@@ -302,17 +343,8 @@ MR_parse_proc_spec(char *str, MR_Proc_Spec *spec)
/*
** 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,29 @@ MR_parse_proc_spec(char *str, MR_Proc_Spec *spec)
return MR_TRUE;
}
/*
** Convert all occurrences of `__' to `:'.
*/
static void
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';
}
/*
** Go backwards over a string starting at `end', stopping at `start',
** parsing the trailing integer and storing it in `*n'.
@@ -474,6 +529,334 @@ MR_process_matching_procedures_in_module(const MR_Module_Layout *module,
}
}
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_copy_string(word);
MR_translate_double_underscores(data->MR_complete_name);
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
MR_print_proc_id_for_debugger(FILE *fp, const MR_Proc_Layout *entry_layout)
{

View File

@@ -1,5 +1,5 @@
/*
** Copyright (C) 1998-2001 The University of Melbourne.
** Copyright (C) 1998-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.
*/
@@ -15,6 +15,7 @@
#define MERCURY_TRACE_TABLES_H
#include "mercury_stack_layout.h"
#include "mercury_trace_completion.h"
#include <stdio.h>
/*
@@ -159,4 +160,10 @@ extern void MR_proc_layout_stats(FILE *fp);
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 */

View File

@@ -125,6 +125,8 @@ static char * MR_trace_browse_var(FILE *out, MR_Var_Details *var,
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);
@@ -1153,6 +1155,32 @@ MR_trace_browse_var(FILE *out, MR_Var_Details *var, char *path,
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;
}
static int
MR_trace_print_var_name(FILE *out, MR_Var_Details *var)
{

View File

@@ -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);
@@ -192,4 +193,8 @@ extern const char *MR_trace_browse_all_on_level(FILE *out,
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 */