mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
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:
3
NEWS
3
NEWS
@@ -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
|
||||
|
||||
@@ -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)
|
||||
**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
@@ -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)))
|
||||
|
||||
@@ -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() */
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
96
tests/debugger/completion.exp
Normal file
96
tests/debugger/completion.exp
Normal 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
|
||||
13
tests/debugger/completion.inp
Normal file
13
tests/debugger/completion.inp
Normal 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
|
||||
9
tests/debugger/completion.inputrc
Normal file
9
tests/debugger/completion.inputrc
Normal 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
|
||||
20
tests/debugger/completion.m
Normal file
20
tests/debugger/completion.m
Normal 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.
|
||||
13
tests/debugger/completion.sub1.m
Normal file
13
tests/debugger/completion.sub1.m
Normal file
@@ -0,0 +1,13 @@
|
||||
:- module completion__sub1.
|
||||
|
||||
:- interface.
|
||||
|
||||
:- func z1 = int.
|
||||
|
||||
:- pred zp(int::out) is det.
|
||||
|
||||
:- implementation.
|
||||
|
||||
z1 = 1.
|
||||
|
||||
zp(1).
|
||||
11
tests/debugger/completion.sub2.m
Normal file
11
tests/debugger/completion.sub2.m
Normal file
@@ -0,0 +1,11 @@
|
||||
:- module completion__sub2.
|
||||
|
||||
:- interface.
|
||||
|
||||
:- include_module completion__sub2__sub3.
|
||||
|
||||
:- func z2 = int.
|
||||
|
||||
:- implementation.
|
||||
|
||||
z2 = 2.
|
||||
9
tests/debugger/completion.sub2.sub3.m
Normal file
9
tests/debugger/completion.sub2.sub3.m
Normal file
@@ -0,0 +1,9 @@
|
||||
:- module completion__sub2__sub3.
|
||||
|
||||
:- interface.
|
||||
|
||||
:- func zabc3 = int.
|
||||
|
||||
:- implementation.
|
||||
|
||||
zabc3 = 3.
|
||||
@@ -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 \
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
574
trace/mercury_trace_completion.c
Normal file
574
trace/mercury_trace_completion.c
Normal 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)
|
||||
{
|
||||
}
|
||||
|
||||
103
trace/mercury_trace_completion.h
Normal file
103
trace/mercury_trace_completion.h
Normal 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
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user